diff --git a/oauth2_http/java/com/google/auth/oauth2/ExternalAccountAuthorizedUserCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ExternalAccountAuthorizedUserCredentials.java
index e67ddb89d..abb1da71c 100644
--- a/oauth2_http/java/com/google/auth/oauth2/ExternalAccountAuthorizedUserCredentials.java
+++ b/oauth2_http/java/com/google/auth/oauth2/ExternalAccountAuthorizedUserCredentials.java
@@ -31,7 +31,9 @@
package com.google.auth.oauth2;
+import static com.google.auth.oauth2.OAuth2Utils.IAM_CREDENTIALS_ALLOWED_LOCATIONS_URL_FORMAT_WORKFORCE_POOL;
import static com.google.auth.oauth2.OAuth2Utils.JSON_FACTORY;
+import static com.google.auth.oauth2.OAuth2Utils.WORKFORCE_AUDIENCE_PATTERN;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.api.client.http.GenericUrl;
@@ -44,6 +46,7 @@
import com.google.api.client.json.JsonObjectParser;
import com.google.api.client.util.GenericData;
import com.google.api.client.util.Preconditions;
+import com.google.api.core.InternalApi;
import com.google.auth.http.HttpTransportFactory;
import com.google.common.base.MoreObjects;
import com.google.common.io.BaseEncoding;
@@ -55,6 +58,7 @@
import java.util.Date;
import java.util.Map;
import java.util.Objects;
+import java.util.regex.Matcher;
import javax.annotation.Nullable;
/**
@@ -75,12 +79,12 @@
* }
*
*/
-public class ExternalAccountAuthorizedUserCredentials extends GoogleCredentials {
+public class ExternalAccountAuthorizedUserCredentials extends GoogleCredentials
+ implements TrustBoundaryProvider {
private static final String PARSE_ERROR_PREFIX = "Error parsing token refresh response. ";
private static final long serialVersionUID = -2181779590486283287L;
-
private final String transportFactoryClassName;
private final String audience;
private final String tokenUrl;
@@ -210,10 +214,27 @@ public AccessToken refreshAccessToken() throws IOException {
this.refreshToken = refreshToken;
}
- return AccessToken.newBuilder()
- .setExpirationTime(expiresAtMilliseconds)
- .setTokenValue(accessToken)
- .build();
+ AccessToken newAccessToken =
+ AccessToken.newBuilder()
+ .setExpirationTime(expiresAtMilliseconds)
+ .setTokenValue(accessToken)
+ .build();
+
+ refreshTrustBoundary(newAccessToken, transportFactory);
+ return newAccessToken;
+ }
+
+ @InternalApi
+ @Override
+ public String getTrustBoundaryUrl() throws IOException {
+ Matcher matcher = WORKFORCE_AUDIENCE_PATTERN.matcher(getAudience());
+ if (!matcher.matches()) {
+ throw new IllegalStateException(
+ "The provided audience is not in the correct format for a workforce pool.");
+ }
+ String poolId = matcher.group("pool");
+ return String.format(
+ IAM_CREDENTIALS_ALLOWED_LOCATIONS_URL_FORMAT_WORKFORCE_POOL, getUniverseDomain(), poolId);
}
@Nullable
diff --git a/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java
index c4268d167..33cbb592c 100644
--- a/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java
+++ b/oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java
@@ -31,12 +31,15 @@
package com.google.auth.oauth2;
+import static com.google.auth.oauth2.OAuth2Utils.WORKFORCE_AUDIENCE_PATTERN;
+import static com.google.auth.oauth2.OAuth2Utils.WORKLOAD_AUDIENCE_PATTERN;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.api.client.http.HttpHeaders;
import com.google.api.client.json.GenericJson;
import com.google.api.client.json.JsonObjectParser;
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;
@@ -55,6 +58,7 @@
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.Executor;
+import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
@@ -64,7 +68,8 @@
*
Handles initializing external credentials, calls to the Security Token Service, and service
* account impersonation.
*/
-public abstract class ExternalAccountCredentials extends GoogleCredentials {
+public abstract class ExternalAccountCredentials extends GoogleCredentials
+ implements TrustBoundaryProvider {
private static final long serialVersionUID = 8049126194174465023L;
@@ -527,7 +532,11 @@ protected AccessToken exchangeExternalCredentialForAccessToken(
this.impersonatedCredentials = this.buildImpersonatedCredentials();
}
if (this.impersonatedCredentials != null) {
- return this.impersonatedCredentials.refreshAccessToken();
+ AccessToken accessToken = this.impersonatedCredentials.refreshAccessToken();
+ // After the impersonated credential refreshes, its trust boundary is
+ // also refreshed. That is the trust boundary we will use.
+ this.trustBoundary = this.impersonatedCredentials.getTrustBoundary();
+ return accessToken;
}
StsRequestHandler.Builder requestHandler =
@@ -556,7 +565,9 @@ protected AccessToken exchangeExternalCredentialForAccessToken(
}
StsTokenExchangeResponse response = requestHandler.build().exchangeToken();
- return response.getAccessToken();
+ AccessToken accessToken = response.getAccessToken();
+ refreshTrustBoundary(accessToken, transportFactory);
+ return accessToken;
}
/**
@@ -613,6 +624,33 @@ public String getServiceAccountEmail() {
return ImpersonatedCredentials.extractTargetPrincipal(serviceAccountImpersonationUrl);
}
+ @InternalApi
+ @Override
+ public String getTrustBoundaryUrl() {
+ Matcher workforceMatcher = WORKFORCE_AUDIENCE_PATTERN.matcher(getAudience());
+ if (workforceMatcher.matches()) {
+ String poolId = workforceMatcher.group("pool");
+ return String.format(
+ OAuth2Utils.IAM_CREDENTIALS_ALLOWED_LOCATIONS_URL_FORMAT_WORKFORCE_POOL,
+ getUniverseDomain(),
+ poolId);
+ }
+
+ Matcher workloadMatcher = WORKLOAD_AUDIENCE_PATTERN.matcher(getAudience());
+ if (workloadMatcher.matches()) {
+ String projectNumber = workloadMatcher.group("project");
+ String poolId = workloadMatcher.group("pool");
+ return String.format(
+ OAuth2Utils.IAM_CREDENTIALS_ALLOWED_LOCATIONS_URL_FORMAT_WORKLOAD_POOL,
+ getUniverseDomain(),
+ projectNumber,
+ poolId);
+ }
+
+ throw new IllegalStateException(
+ "The provided audience is not in a valid format for either a workload identity pool or a workforce pool.");
+ }
+
@Nullable
public String getClientId() {
return clientId;
diff --git a/oauth2_http/java/com/google/auth/oauth2/GoogleCredentials.java b/oauth2_http/java/com/google/auth/oauth2/GoogleCredentials.java
index 59afc00d5..4f3118bc8 100644
--- a/oauth2_http/java/com/google/auth/oauth2/GoogleCredentials.java
+++ b/oauth2_http/java/com/google/auth/oauth2/GoogleCredentials.java
@@ -108,7 +108,7 @@ String getFileType() {
private final String universeDomain;
private final boolean isExplicitUniverseDomain;
- private TrustBoundary trustBoundary;
+ TrustBoundary trustBoundary;
protected final String quotaProjectId;
@@ -339,6 +339,10 @@ TrustBoundary getTrustBoundary() {
return trustBoundary;
}
+ protected void setTrustBoundary(TrustBoundary trustBoundary) {
+ this.trustBoundary = trustBoundary;
+ }
+
/**
* Refreshes the trust boundary by making a call to the trust boundary URL.
*
diff --git a/oauth2_http/java/com/google/auth/oauth2/OAuth2Utils.java b/oauth2_http/java/com/google/auth/oauth2/OAuth2Utils.java
index b69d636c8..72cab4f01 100644
--- a/oauth2_http/java/com/google/auth/oauth2/OAuth2Utils.java
+++ b/oauth2_http/java/com/google/auth/oauth2/OAuth2Utils.java
@@ -68,6 +68,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.regex.Pattern;
/**
* Internal utilities for the com.google.auth.oauth2 namespace.
@@ -93,9 +94,6 @@ public class OAuth2Utils {
static final URI TOKEN_SERVER_URI = URI.create("https://oauth2.googleapis.com/token");
static final URI TOKEN_REVOKE_URI = URI.create("https://oauth2.googleapis.com/revoke");
-
- static final String IAM_CREDENTIALS_ALLOWED_LOCATIONS_URL_FORMAT_SERVICE_ACCOUNT =
- "https://iamcredentials.%s/v1/projects/-/serviceAccounts/%s/allowedLocations";
static final URI USER_AUTH_URI = URI.create("https://accounts.google.com/o/oauth2/auth");
public static final String CLOUD_PLATFORM_SCOPE =
@@ -120,6 +118,22 @@ public class OAuth2Utils {
static final double RETRY_MULTIPLIER = 2;
static final int DEFAULT_NUMBER_OF_RETRIES = 3;
+ static final Pattern WORKFORCE_AUDIENCE_PATTERN =
+ Pattern.compile(
+ "^//iam.googleapis.com/locations/(?[^/]+)/workforcePools/(?[^/]+)/providers/(?[^/]+)$");
+ static final Pattern WORKLOAD_AUDIENCE_PATTERN =
+ Pattern.compile(
+ "^//iam.googleapis.com/projects/(?[^/]+)/locations/(?[^/]+)/workloadIdentityPools/(?[^/]+)/providers/(?[^/]+)$");
+
+ static final String IAM_CREDENTIALS_ALLOWED_LOCATIONS_URL_FORMAT_SERVICE_ACCOUNT =
+ "https://iamcredentials.%s/v1/projects/-/serviceAccounts/%s/allowedLocations";
+
+ static final String IAM_CREDENTIALS_ALLOWED_LOCATIONS_URL_FORMAT_WORKFORCE_POOL =
+ "https://iamcredentials.%s/v1/locations/global/workforcePools/%s/allowedLocations";
+
+ static final String IAM_CREDENTIALS_ALLOWED_LOCATIONS_URL_FORMAT_WORKLOAD_POOL =
+ "https://iamcredentials.%s/v1/projects/%s/locations/global/workloadIdentityPools/%s/allowedLocations";
+
// Includes expected server errors from Google token endpoint
// Other 5xx codes are either not used or retries are unlikely to succeed
public static final Set TOKEN_ENDPOINT_RETRYABLE_STATUS_CODES =
diff --git a/oauth2_http/java/com/google/auth/oauth2/TrustBoundary.java b/oauth2_http/java/com/google/auth/oauth2/TrustBoundary.java
index 672941e58..ebbb110de 100644
--- a/oauth2_http/java/com/google/auth/oauth2/TrustBoundary.java
+++ b/oauth2_http/java/com/google/auth/oauth2/TrustBoundary.java
@@ -184,9 +184,7 @@ static TrustBoundary refresh(
// Add the cached trust boundary header, if available.
if (cachedTrustBoundary != null) {
- String headerValue =
- cachedTrustBoundary.isNoOp() ? "" : cachedTrustBoundary.getEncodedLocations();
- request.getHeaders().set(TRUST_BOUNDARY_KEY, headerValue);
+ request.getHeaders().set(TRUST_BOUNDARY_KEY, cachedTrustBoundary.getEncodedLocations());
}
// Add retry logic
diff --git a/oauth2_http/javatests/com/google/auth/TestUtils.java b/oauth2_http/javatests/com/google/auth/TestUtils.java
index 99d601da8..b68b49dc5 100644
--- a/oauth2_http/javatests/com/google/auth/TestUtils.java
+++ b/oauth2_http/javatests/com/google/auth/TestUtils.java
@@ -42,6 +42,7 @@
import com.google.api.client.json.gson.GsonFactory;
import com.google.auth.http.AuthHttpConstants;
import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@@ -55,6 +56,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.TimeZone;
import javax.annotation.Nullable;
/** Utilities for test code under com.google.auth. */
@@ -64,6 +66,9 @@ public class TestUtils {
URI.create("https://auth.cloud.google/authorize");
public static final URI WORKFORCE_IDENTITY_FEDERATION_TOKEN_SERVER_URI =
URI.create("https://sts.googleapis.com/v1/oauthtoken");
+ public static final String TRUST_BOUNDARY_ENCODED_LOCATION = "0x800000";
+ public static final List TRUST_BOUNDARY_LOCATIONS =
+ ImmutableList.of("us-central1", "us-central2");
private static final JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance();
@@ -147,7 +152,9 @@ public static String getDefaultExpireTime() {
Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date());
calendar.add(Calendar.SECOND, 300);
- return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").format(calendar.getTime());
+ SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ return dateFormat.format(calendar.getTime());
}
private TestUtils() {}
diff --git a/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java
index e8b401063..795a1207a 100644
--- a/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java
+++ b/oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java
@@ -1399,4 +1399,34 @@ public AwsSecurityCredentials getCredentials(ExternalAccountSupplierContext cont
return credentials;
}
}
+
+ @Test
+ public void testRefresh_trustBoundarySuccess() throws IOException {
+ TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider();
+ TrustBoundary.setEnvironmentProviderForTest(environmentProvider);
+ environmentProvider.setEnv("GOOGLE_AUTH_TRUST_BOUNDARY_ENABLE_EXPERIMENT", "1");
+
+ MockExternalAccountCredentialsTransportFactory transportFactory =
+ new MockExternalAccountCredentialsTransportFactory();
+
+ AwsSecurityCredentialsSupplier supplier =
+ new TestAwsSecurityCredentialsSupplier("test", programmaticAwsCreds, null, null);
+
+ AwsCredentials awsCredential =
+ AwsCredentials.newBuilder()
+ .setAwsSecurityCredentialsSupplier(supplier)
+ .setHttpTransportFactory(transportFactory)
+ .setAudience(
+ "//iam.googleapis.com/projects/12345/locations/global/workloadIdentityPools/pool/providers/provider")
+ .setTokenUrl(STS_URL)
+ .setSubjectTokenType("subjectTokenType")
+ .build();
+
+ awsCredential.refreshAccessToken();
+
+ TrustBoundary trustBoundary = awsCredential.getTrustBoundary();
+ assertNotNull(trustBoundary);
+ assertEquals(TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION, trustBoundary.getEncodedLocations());
+ TrustBoundary.setEnvironmentProviderForTest(null);
+ }
}
diff --git a/oauth2_http/javatests/com/google/auth/oauth2/ComputeEngineCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/ComputeEngineCredentialsTest.java
index 275545db2..e13c9ce93 100644
--- a/oauth2_http/javatests/com/google/auth/oauth2/ComputeEngineCredentialsTest.java
+++ b/oauth2_http/javatests/com/google/auth/oauth2/ComputeEngineCredentialsTest.java
@@ -33,6 +33,7 @@
import static com.google.auth.oauth2.ComputeEngineCredentials.METADATA_RESPONSE_EMPTY_CONTENT_ERROR_MESSAGE;
import static com.google.auth.oauth2.ImpersonatedCredentialsTest.SA_CLIENT_EMAIL;
+import static com.google.auth.oauth2.TrustBoundary.TRUST_BOUNDARY_KEY;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -1159,7 +1160,8 @@ public void refresh_trustBoundarySuccess() throws IOException {
String defaultAccountEmail = "default@email.com";
MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory();
TrustBoundary trustBoundary =
- new TrustBoundary("0x80000", Collections.singletonList("us-central1"));
+ new TrustBoundary(
+ TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION, TestUtils.TRUST_BOUNDARY_LOCATIONS);
transportFactory.transport.setTrustBoundary(trustBoundary);
transportFactory.transport.setServiceAccountEmail(defaultAccountEmail);
@@ -1167,7 +1169,8 @@ public void refresh_trustBoundarySuccess() throws IOException {
ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build();
Map> headers = credentials.getRequestMetadata();
- assertEquals(headers.get("x-allowed-locations"), Arrays.asList("0x80000"));
+ assertEquals(
+ headers.get(TRUST_BOUNDARY_KEY), Arrays.asList(TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION));
}
@Test
diff --git a/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountAuthorizedUserCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountAuthorizedUserCredentialsTest.java
index 740cabba5..04673a260 100644
--- a/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountAuthorizedUserCredentialsTest.java
+++ b/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountAuthorizedUserCredentialsTest.java
@@ -35,8 +35,10 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -62,6 +64,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -132,6 +135,11 @@ public void setup() {
transportFactory = new MockExternalAccountAuthorizedUserCredentialsTransportFactory();
}
+ @After
+ public void tearDown() {
+ TrustBoundary.setEnvironmentProviderForTest(null);
+ }
+
@Test
public void builder_allFields() throws IOException {
ExternalAccountAuthorizedUserCredentials credentials =
@@ -1216,6 +1224,55 @@ public void toString_expectedFormat() {
assertEquals(expectedToString, credentials.toString());
}
+ @Test
+ public void testRefresh_trustBoundarySuccess() throws IOException {
+ TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider();
+ TrustBoundary.setEnvironmentProviderForTest(environmentProvider);
+ environmentProvider.setEnv("GOOGLE_AUTH_TRUST_BOUNDARY_ENABLE_EXPERIMENT", "1");
+
+ ExternalAccountAuthorizedUserCredentials credentials =
+ ExternalAccountAuthorizedUserCredentials.newBuilder()
+ .setHttpTransportFactory(transportFactory)
+ .setAudience(AUDIENCE)
+ .setClientId(CLIENT_ID)
+ .setClientSecret(CLIENT_SECRET)
+ .setRefreshToken(REFRESH_TOKEN)
+ .setTokenUrl(TOKEN_URL)
+ .build();
+
+ credentials.refresh();
+ TrustBoundary trustBoundary = credentials.getTrustBoundary();
+ assertNotNull(trustBoundary);
+ assertEquals(TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION, trustBoundary.getEncodedLocations());
+ }
+
+ @Test
+ public void testRefresh_trustBoundaryFails_incorrectAudience() {
+ TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider();
+ TrustBoundary.setEnvironmentProviderForTest(environmentProvider);
+ environmentProvider.setEnv("GOOGLE_AUTH_TRUST_BOUNDARY_ENABLE_EXPERIMENT", "1");
+
+ ExternalAccountAuthorizedUserCredentials credentials =
+ ExternalAccountAuthorizedUserCredentials.newBuilder()
+ .setHttpTransportFactory(transportFactory)
+ .setAudience("audience")
+ .setClientId(CLIENT_ID)
+ .setClientSecret(CLIENT_SECRET)
+ .setRefreshToken(REFRESH_TOKEN)
+ .setTokenUrl(TOKEN_URL)
+ .build();
+
+ IllegalStateException exception =
+ assertThrows(
+ IllegalStateException.class,
+ () -> {
+ credentials.refresh();
+ });
+ assertEquals(
+ "The provided audience is not in the correct format for a workforce pool.",
+ exception.getMessage());
+ }
+
@Test
public void serialize() throws IOException, ClassNotFoundException {
ExternalAccountAuthorizedUserCredentials credentials =
diff --git a/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountCredentialsTest.java
index 32009f755..14fe4e90b 100644
--- a/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountCredentialsTest.java
+++ b/oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountCredentialsTest.java
@@ -36,6 +36,7 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -56,6 +57,7 @@
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -93,6 +95,11 @@ public void setup() {
transportFactory = new MockExternalAccountCredentialsTransportFactory();
}
+ @After
+ public void tearDown() {
+ TrustBoundary.setEnvironmentProviderForTest(null);
+ }
+
@Test
public void fromStream_identityPoolCredentials() throws IOException {
GenericJson json = buildJsonIdentityPoolCredential();
@@ -1248,6 +1255,194 @@ public void validateServiceAccountImpersonationUrls_invalidUrls() {
}
}
+ @Test
+ public void getTrustBoundaryUrl_workload() throws IOException {
+ String audience =
+ "//iam.googleapis.com/projects/12345/locations/global/workloadIdentityPools/my-pool/providers/my-provider";
+ ExternalAccountCredentials credentials =
+ TestExternalAccountCredentials.newBuilder()
+ .setAudience(audience)
+ .setSubjectTokenType("subject_token_type")
+ .setCredentialSource(new TestCredentialSource(FILE_CREDENTIAL_SOURCE_MAP))
+ .build();
+
+ String expectedUrl =
+ "https://iamcredentials.googleapis.com/v1/projects/12345/locations/global/workloadIdentityPools/my-pool/allowedLocations";
+ assertEquals(expectedUrl, credentials.getTrustBoundaryUrl());
+ }
+
+ @Test
+ public void getTrustBoundaryUrl_workforce() throws IOException {
+ String audience =
+ "//iam.googleapis.com/locations/global/workforcePools/my-pool/providers/my-provider";
+ ExternalAccountCredentials credentials =
+ TestExternalAccountCredentials.newBuilder()
+ .setAudience(audience)
+ .setWorkforcePoolUserProject("12345")
+ .setSubjectTokenType("subject_token_type")
+ .setCredentialSource(new TestCredentialSource(FILE_CREDENTIAL_SOURCE_MAP))
+ .build();
+
+ String expectedUrl =
+ "https://iamcredentials.googleapis.com/v1/locations/global/workforcePools/my-pool/allowedLocations";
+ assertEquals(expectedUrl, credentials.getTrustBoundaryUrl());
+ }
+
+ @Test
+ public void getTrustBoundaryUrl_invalidAudience_throws() {
+ ExternalAccountCredentials credentials =
+ TestExternalAccountCredentials.newBuilder()
+ .setAudience("invalid-audience")
+ .setSubjectTokenType("subject_token_type")
+ .setCredentialSource(new TestCredentialSource(FILE_CREDENTIAL_SOURCE_MAP))
+ .build();
+
+ IllegalStateException exception =
+ assertThrows(
+ IllegalStateException.class,
+ () -> {
+ credentials.getTrustBoundaryUrl();
+ });
+
+ assertEquals(
+ "The provided audience is not in a valid format for either a workload identity pool or a workforce pool.",
+ exception.getMessage());
+ }
+
+ @Test
+ public void refresh_workload_trustBoundarySuccess() throws IOException {
+ String audience =
+ "//iam.googleapis.com/projects/12345/locations/global/workloadIdentityPools/my-pool/providers/my-provider";
+
+ TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider();
+ TrustBoundary.setEnvironmentProviderForTest(environmentProvider);
+ environmentProvider.setEnv("GOOGLE_AUTH_TRUST_BOUNDARY_ENABLE_EXPERIMENT", "1");
+
+ ExternalAccountCredentials credentials =
+ new IdentityPoolCredentials(
+ IdentityPoolCredentials.newBuilder()
+ .setHttpTransportFactory(transportFactory)
+ .setAudience(audience)
+ .setSubjectTokenType("subject_token_type")
+ .setTokenUrl(STS_URL)
+ .setCredentialSource(new TestCredentialSource(FILE_CREDENTIAL_SOURCE_MAP))
+ .setEnvironmentProvider(environmentProvider)) {
+ @Override
+ public String retrieveSubjectToken() throws IOException {
+ // This override isolates the test from the filesystem.
+ return "dummy-subject-token";
+ }
+ };
+ credentials.refresh();
+
+ TrustBoundary trustBoundary = credentials.getTrustBoundary();
+ assertNotNull(trustBoundary);
+ assertEquals(TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION, trustBoundary.getEncodedLocations());
+ }
+
+ @Test
+ public void refresh_workforce_trustBoundarySuccess() throws IOException {
+ String audience =
+ "//iam.googleapis.com/locations/global/workforcePools/my-pool/providers/my-provider";
+
+ TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider();
+ TrustBoundary.setEnvironmentProviderForTest(environmentProvider);
+ environmentProvider.setEnv("GOOGLE_AUTH_TRUST_BOUNDARY_ENABLE_EXPERIMENT", "1");
+
+ ExternalAccountCredentials credentials =
+ new IdentityPoolCredentials(
+ IdentityPoolCredentials.newBuilder()
+ .setHttpTransportFactory(transportFactory)
+ .setAudience(audience)
+ .setWorkforcePoolUserProject("12345")
+ .setSubjectTokenType("subject_token_type")
+ .setTokenUrl(STS_URL)
+ .setCredentialSource(new TestCredentialSource(FILE_CREDENTIAL_SOURCE_MAP))
+ .setEnvironmentProvider(environmentProvider)) {
+ @Override
+ public String retrieveSubjectToken() throws IOException {
+ return "dummy-subject-token";
+ }
+ };
+
+ credentials.refresh();
+
+ TrustBoundary trustBoundary = credentials.getTrustBoundary();
+ assertNotNull(trustBoundary);
+ assertEquals(TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION, trustBoundary.getEncodedLocations());
+ }
+
+ @Test
+ public void refresh_impersonated_workload_trustBoundarySuccess() throws IOException {
+ String audience =
+ "//iam.googleapis.com/projects/12345/locations/global/workloadIdentityPools/my-pool/providers/my-provider";
+ TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider();
+ TrustBoundary.setEnvironmentProviderForTest(environmentProvider);
+ environmentProvider.setEnv("GOOGLE_AUTH_TRUST_BOUNDARY_ENABLE_EXPERIMENT", "1");
+
+ transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime());
+
+ // Use a URL-based source that the mock transport can handle, to avoid file IO.
+ Map urlCredentialSourceMap = new HashMap<>();
+ urlCredentialSourceMap.put("url", "https://www.metadata.google.com");
+ Map headers = new HashMap<>();
+ headers.put("Metadata-Flavor", "Google");
+ urlCredentialSourceMap.put("headers", headers);
+
+ ExternalAccountCredentials credentials =
+ IdentityPoolCredentials.newBuilder()
+ .setHttpTransportFactory(transportFactory)
+ .setAudience(audience)
+ .setSubjectTokenType("subject_token_type")
+ .setTokenUrl(STS_URL)
+ .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL)
+ .setCredentialSource(new IdentityPoolCredentialSource(urlCredentialSourceMap))
+ .setEnvironmentProvider(environmentProvider)
+ .build();
+
+ credentials.refresh();
+
+ TrustBoundary trustBoundary = credentials.getTrustBoundary();
+ assertNotNull(trustBoundary);
+ assertEquals(TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION, trustBoundary.getEncodedLocations());
+ }
+
+ @Test
+ public void refresh_impersonated_workforce_trustBoundarySuccess() throws IOException {
+ String audience =
+ "//iam.googleapis.com/locations/global/workforcePools/my-pool/providers/my-provider";
+ TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider();
+ TrustBoundary.setEnvironmentProviderForTest(environmentProvider);
+ environmentProvider.setEnv("GOOGLE_AUTH_TRUST_BOUNDARY_ENABLE_EXPERIMENT", "1");
+
+ transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime());
+
+ // Use a URL-based source that the mock transport can handle, to avoid file IO.
+ Map urlCredentialSourceMap = new HashMap<>();
+ urlCredentialSourceMap.put("url", "https://www.metadata.google.com");
+ Map headers = new HashMap<>();
+ headers.put("Metadata-Flavor", "Google");
+ urlCredentialSourceMap.put("headers", headers);
+
+ ExternalAccountCredentials credentials =
+ IdentityPoolCredentials.newBuilder()
+ .setHttpTransportFactory(transportFactory)
+ .setAudience(audience)
+ .setWorkforcePoolUserProject("12345")
+ .setSubjectTokenType("subject_token_type")
+ .setTokenUrl(STS_URL)
+ .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL)
+ .setCredentialSource(new IdentityPoolCredentialSource(urlCredentialSourceMap))
+ .setEnvironmentProvider(environmentProvider)
+ .build();
+
+ credentials.refresh();
+
+ TrustBoundary trustBoundary = credentials.getTrustBoundary();
+ assertNotNull(trustBoundary);
+ assertEquals(TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION, trustBoundary.getEncodedLocations());
+ }
+
private GenericJson buildJsonIdentityPoolCredential() {
GenericJson json = new GenericJson();
json.put(
diff --git a/oauth2_http/javatests/com/google/auth/oauth2/GoogleCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/GoogleCredentialsTest.java
index b8f8c76e9..edf8b104a 100644
--- a/oauth2_http/javatests/com/google/auth/oauth2/GoogleCredentialsTest.java
+++ b/oauth2_http/javatests/com/google/auth/oauth2/GoogleCredentialsTest.java
@@ -31,6 +31,7 @@
package com.google.auth.oauth2;
+import static com.google.auth.oauth2.TrustBoundary.TRUST_BOUNDARY_KEY;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -977,7 +978,8 @@ public void trustBoundary_shouldFetchAndReturnTrustBoundaryDataSuccessfully() th
MockTokenServerTransport transport = new MockTokenServerTransport();
transport.addServiceAccount(SA_CLIENT_EMAIL, ACCESS_TOKEN);
TrustBoundary trustBoundary =
- new TrustBoundary("0x80000", Collections.singletonList("us-central1"));
+ new TrustBoundary(
+ TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION, Collections.singletonList("us-central1"));
transport.setTrustBoundary(trustBoundary);
ServiceAccountCredentials credentials =
@@ -990,7 +992,8 @@ public void trustBoundary_shouldFetchAndReturnTrustBoundaryDataSuccessfully() th
.build();
Map> headers = credentials.getRequestMetadata();
- assertEquals(headers.get("x-allowed-locations"), Arrays.asList("0x80000"));
+ assertEquals(
+ headers.get(TRUST_BOUNDARY_KEY), Arrays.asList(TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION));
}
@Test
@@ -1004,7 +1007,8 @@ public void trustBoundary_shouldRetryTrustBoundaryLookupOnFailure() throws IOExc
MockTokenServerTransport trustBoundaryTransport = new MockTokenServerTransport();
trustBoundaryTransport.addResponseErrorSequence(new IOException("Service Unavailable"));
TrustBoundary trustBoundary =
- new TrustBoundary("0x80000", Collections.singletonList("us-central1"));
+ new TrustBoundary(
+ TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION, TestUtils.TRUST_BOUNDARY_LOCATIONS);
trustBoundaryTransport.setTrustBoundary(trustBoundary);
// This transport will be used for the access token refresh.
@@ -1034,7 +1038,8 @@ public com.google.api.client.http.LowLevelHttpRequest buildRequest(
.build();
Map> headers = credentials.getRequestMetadata();
- assertEquals(headers.get("x-allowed-locations"), Arrays.asList("0x80000"));
+ assertEquals(
+ Arrays.asList(TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION), headers.get(TRUST_BOUNDARY_KEY));
}
@Test
@@ -1135,7 +1140,8 @@ public void trustBoundary_refreshShouldReturnNoOpAndNotCallLookupEndpointWhenCac
// Set trust boundary to a valid non No-Op value.
transport.setTrustBoundary(
- new TrustBoundary("0x80000", Collections.singletonList("us-central1")));
+ new TrustBoundary(
+ TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION, TestUtils.TRUST_BOUNDARY_LOCATIONS));
// Refresh trust boundaries
credentials.refresh();
@@ -1153,7 +1159,8 @@ public void trustBoundary_refreshShouldReturnCachedTbIfCallToLookupFails() throw
MockTokenServerTransport transport = new MockTokenServerTransport();
transport.addServiceAccount(SA_CLIENT_EMAIL, ACCESS_TOKEN);
TrustBoundary trustBoundary =
- new TrustBoundary("0x80000", Collections.singletonList("us-central1"));
+ new TrustBoundary(
+ TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION, TestUtils.TRUST_BOUNDARY_LOCATIONS);
transport.setTrustBoundary(trustBoundary);
ServiceAccountCredentials credentials =
@@ -1174,7 +1181,8 @@ public void trustBoundary_refreshShouldReturnCachedTbIfCallToLookupFails() throw
credentials.refresh();
assertEquals(
- trustBoundary.getEncodedLocations(), credentials.getTrustBoundary().getEncodedLocations());
+ TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION,
+ credentials.getTrustBoundary().getEncodedLocations());
}
@Test
@@ -1239,7 +1247,8 @@ public void trustBoundary_getRequestHeadersShouldAttachTrustBoundaryHeader() thr
MockTokenServerTransport transport = new MockTokenServerTransport();
transport.addServiceAccount(SA_CLIENT_EMAIL, ACCESS_TOKEN);
TrustBoundary trustBoundary =
- new TrustBoundary("0x80000", Collections.singletonList("us-central1"));
+ new TrustBoundary(
+ TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION, Collections.singletonList("us-central1"));
transport.setTrustBoundary(trustBoundary);
ServiceAccountCredentials credentials =
@@ -1253,7 +1262,8 @@ public void trustBoundary_getRequestHeadersShouldAttachTrustBoundaryHeader() thr
Map> headers = credentials.getRequestMetadata();
- assertEquals(Arrays.asList("0x80000"), headers.get("x-allowed-locations"));
+ assertEquals(
+ Arrays.asList(TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION), headers.get(TRUST_BOUNDARY_KEY));
}
@Test
@@ -1278,7 +1288,7 @@ public void trustBoundary_getRequestHeadersShouldAttachEmptyStringTbHeaderInCase
Map> headers = credentials.getRequestMetadata();
- assertEquals(Arrays.asList(""), headers.get("x-allowed-locations"));
+ assertEquals(Arrays.asList(""), headers.get(TRUST_BOUNDARY_KEY));
}
@Test
@@ -1303,6 +1313,6 @@ public void trustBoundary_getRequestHeadersShouldNotAttachTbHeaderInCaseOfNonGdu
Map> headers = credentials.getRequestMetadata();
- assertNull(headers.get("x-allowed-locations"));
+ assertNull(headers.get(TRUST_BOUNDARY_KEY));
}
}
diff --git a/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java
index cce03e085..a48e56c7e 100644
--- a/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java
+++ b/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java
@@ -1304,4 +1304,32 @@ void setShouldThrowOnGetCertificatePath(boolean shouldThrow) {
this.shouldThrowOnGetCertificatePath = shouldThrow;
}
}
+
+ @Test
+ public void testRefresh_trustBoundarySuccess() throws IOException {
+ TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider();
+ TrustBoundary.setEnvironmentProviderForTest(environmentProvider);
+ environmentProvider.setEnv("GOOGLE_AUTH_TRUST_BOUNDARY_ENABLE_EXPERIMENT", "1");
+
+ MockExternalAccountCredentialsTransportFactory transportFactory =
+ new MockExternalAccountCredentialsTransportFactory();
+ HttpTransportFactory testingHttpTransportFactory = transportFactory;
+
+ IdentityPoolCredentials credentials =
+ IdentityPoolCredentials.newBuilder()
+ .setSubjectTokenSupplier(testProvider)
+ .setHttpTransportFactory(testingHttpTransportFactory)
+ .setAudience(
+ "//iam.googleapis.com/projects/12345/locations/global/workloadIdentityPools/pool/providers/provider")
+ .setSubjectTokenType("subjectTokenType")
+ .setTokenUrl(STS_URL)
+ .build();
+
+ credentials.refresh();
+
+ TrustBoundary trustBoundary = credentials.getTrustBoundary();
+ assertNotNull(trustBoundary);
+ assertEquals(TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION, trustBoundary.getEncodedLocations());
+ TrustBoundary.setEnvironmentProviderForTest(null);
+ }
}
diff --git a/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java
index 87225c464..abe5d4ed7 100644
--- a/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java
+++ b/oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java
@@ -31,6 +31,7 @@
package com.google.auth.oauth2;
+import static com.google.auth.oauth2.TrustBoundary.TRUST_BOUNDARY_KEY;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -67,6 +68,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
+import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
@@ -155,7 +157,8 @@ public class ImpersonatedCredentialsTest extends BaseSerializationTest {
public static final List DELEGATES =
Arrays.asList("sa1@developer.gserviceaccount.com", "sa2@developer.gserviceaccount.com");
public static final TrustBoundary TRUST_BOUNDARY =
- new TrustBoundary("0x80000", Arrays.asList("us-central1"));
+ new TrustBoundary(
+ TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION, TestUtils.TRUST_BOUNDARY_LOCATIONS);
private GoogleCredentials sourceCredentials;
private MockIAMCredentialsServiceTransportFactory mockTransportFactory;
@@ -1338,7 +1341,9 @@ public void refresh_trustBoundarySuccess() throws IOException {
mockTransportFactory);
Map> headers = targetCredentials.getRequestMetadata();
- assertEquals(headers.get("x-allowed-locations"), Arrays.asList("0x80000"));
+ assertEquals(
+ headers.get(TRUST_BOUNDARY_KEY),
+ Collections.singletonList(TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION));
}
@Test
@@ -1347,9 +1352,6 @@ public void refresh_trustBoundaryFails_throwsIOException() throws IOException {
TrustBoundary.setEnvironmentProviderForTest(environmentProvider);
environmentProvider.setEnv("GOOGLE_AUTH_TRUST_BOUNDARY_ENABLE_EXPERIMENT", "1");
- // Mock trust boundary response
- TrustBoundary trustBoundary = TRUST_BOUNDARY;
-
mockTransportFactory.getTransport().setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL);
mockTransportFactory.getTransport().setAccessToken(ACCESS_TOKEN);
mockTransportFactory.getTransport().setExpireTime(getDefaultExpireTime());
diff --git a/oauth2_http/javatests/com/google/auth/oauth2/MockExternalAccountCredentialsTransport.java b/oauth2_http/javatests/com/google/auth/oauth2/MockExternalAccountCredentialsTransport.java
index d1bfdaecf..7be65adf3 100644
--- a/oauth2_http/javatests/com/google/auth/oauth2/MockExternalAccountCredentialsTransport.java
+++ b/oauth2_http/javatests/com/google/auth/oauth2/MockExternalAccountCredentialsTransport.java
@@ -68,6 +68,7 @@ public class MockExternalAccountCredentialsTransport extends MockHttpTransport {
private static final String AWS_IMDSV2_SESSION_TOKEN_URL = "https://169.254.169.254/imdsv2";
private static final String METADATA_SERVER_URL = "https://www.metadata.google.com";
private static final String STS_URL = "https://sts.googleapis.com/v1/token";
+ private static final String TRUST_BOUNDARY_URL_END = "/allowedLocations";
private static final String SUBJECT_TOKEN = "subjectToken";
private static final String TOKEN_TYPE = "Bearer";
@@ -196,6 +197,18 @@ public LowLevelHttpResponse execute() throws IOException {
}
if (url.contains(IAM_ENDPOINT)) {
+
+ if (url.endsWith(TRUST_BOUNDARY_URL_END)) {
+ GenericJson responseJson = new GenericJson();
+ responseJson.setFactory(OAuth2Utils.JSON_FACTORY);
+ responseJson.put("encodedLocations", TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION);
+ responseJson.put("locations", TestUtils.TRUST_BOUNDARY_LOCATIONS);
+ String content = responseJson.toPrettyString();
+ return new MockLowLevelHttpResponse()
+ .setContentType(Json.MEDIA_TYPE)
+ .setContent(content);
+ }
+
GenericJson query =
OAuth2Utils.JSON_FACTORY
.createJsonParser(getContentAsString())
diff --git a/oauth2_http/javatests/com/google/auth/oauth2/MockMetadataServerTransport.java b/oauth2_http/javatests/com/google/auth/oauth2/MockMetadataServerTransport.java
index 057d19b94..3e90d0faa 100644
--- a/oauth2_http/javatests/com/google/auth/oauth2/MockMetadataServerTransport.java
+++ b/oauth2_http/javatests/com/google/auth/oauth2/MockMetadataServerTransport.java
@@ -388,8 +388,10 @@ public LowLevelHttpResponse execute() throws IOException {
}
protected boolean isIamLookupUrl(String url) {
- // This is for mocking the call to IAM lookup when we attempt to refresh trust boundaries
- // after refreshing the access token.
+ // Mocking call to the /allowedLocations endpoint for trust boundary refresh.
+ // For testing convenience, this mock transport handles
+ // the /allowedLocations endpoint. The actual server for this endpoint
+ // will be the IAM Credentials API.
return url.endsWith("/allowedLocations");
}
}
diff --git a/oauth2_http/javatests/com/google/auth/oauth2/MockStsTransport.java b/oauth2_http/javatests/com/google/auth/oauth2/MockStsTransport.java
index 5b1b3fded..e3a50d027 100644
--- a/oauth2_http/javatests/com/google/auth/oauth2/MockStsTransport.java
+++ b/oauth2_http/javatests/com/google/auth/oauth2/MockStsTransport.java
@@ -62,6 +62,8 @@ public final class MockStsTransport extends MockHttpTransport {
private static final String ISSUED_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:access_token";
private static final String VALID_STS_PATTERN =
"https:\\/\\/sts.[a-z-_\\.]+\\/v1\\/(token|oauthtoken)";
+ private static final String VALID_TRUST_BOUNDARY_PATTERN =
+ "https:\\/\\/iam.[a-z-_\\.]+\\/v1\\/.*\\/allowedLocations";
private static final String ACCESS_TOKEN = "accessToken";
private static final String TOKEN_TYPE = "Bearer";
private static final Long EXPIRES_IN = 3600L;
@@ -99,6 +101,22 @@ public LowLevelHttpRequest buildRequest(final String method, final String url) {
new MockLowLevelHttpRequest(url) {
@Override
public LowLevelHttpResponse execute() throws IOException {
+ // Mocking call to refresh trust boundaries.
+ // The lookup endpoint is located in the IAM server.
+ Matcher trustBoundaryMatcher =
+ Pattern.compile(VALID_TRUST_BOUNDARY_PATTERN).matcher(url);
+ if (trustBoundaryMatcher.matches()) {
+ // Mocking call to the /allowedLocations endpoint for trust boundary refresh.
+ // For testing convenience, this mock transport handles
+ // the /allowedLocations endpoint.
+ GenericJson response = new GenericJson();
+ response.put("locations", TestUtils.TRUST_BOUNDARY_LOCATIONS);
+ response.put("encodedLocations", TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION);
+ return new MockLowLevelHttpResponse()
+ .setContentType(Json.MEDIA_TYPE)
+ .setContent(OAuth2Utils.JSON_FACTORY.toString(response));
+ }
+
// Environment version is prefixed by "aws". e.g. "aws1".
Matcher matcher = Pattern.compile(VALID_STS_PATTERN).matcher(url);
if (!matcher.matches()) {
diff --git a/oauth2_http/javatests/com/google/auth/oauth2/MockTokenServerTransport.java b/oauth2_http/javatests/com/google/auth/oauth2/MockTokenServerTransport.java
index c0b9df754..76ef3f807 100644
--- a/oauth2_http/javatests/com/google/auth/oauth2/MockTokenServerTransport.java
+++ b/oauth2_http/javatests/com/google/auth/oauth2/MockTokenServerTransport.java
@@ -327,8 +327,10 @@ public LowLevelHttpResponse execute() throws IOException {
};
return request;
} else if (urlWithoutQuery.endsWith("/allowedLocations")) {
- // This is for mocking the call to IAM lookup when we attempt to refresh trust boundaries
- // after refreshing the access token.
+ // Mocking call to the /allowedLocations endpoint for trust boundary refresh.
+ // For testing convenience, this mock transport handles
+ // the /allowedLocations endpoint. The actual server for this endpoint
+ // will be the IAM Credentials API.
request =
new MockLowLevelHttpRequest(url) {
@Override
diff --git a/oauth2_http/javatests/com/google/auth/oauth2/PluggableAuthCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/PluggableAuthCredentialsTest.java
index cd321daf3..fa21acc0e 100644
--- a/oauth2_http/javatests/com/google/auth/oauth2/PluggableAuthCredentialsTest.java
+++ b/oauth2_http/javatests/com/google/auth/oauth2/PluggableAuthCredentialsTest.java
@@ -603,6 +603,34 @@ public void serialize() throws IOException, ClassNotFoundException {
assertThrows(NotSerializableException.class, () -> serializeAndDeserialize(testCredentials));
}
+ @Test
+ public void testRefresh_trustBoundarySuccess() throws IOException {
+ TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider();
+ TrustBoundary.setEnvironmentProviderForTest(environmentProvider);
+ environmentProvider.setEnv("GOOGLE_AUTH_TRUST_BOUNDARY_ENABLE_EXPERIMENT", "1");
+
+ MockExternalAccountCredentialsTransportFactory transportFactory =
+ new MockExternalAccountCredentialsTransportFactory();
+ transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime());
+
+ PluggableAuthCredentials credentials =
+ PluggableAuthCredentials.newBuilder()
+ .setHttpTransportFactory(transportFactory)
+ .setAudience(
+ "//iam.googleapis.com/projects/12345/locations/global/workloadIdentityPools/pool/providers/provider")
+ .setSubjectTokenType("subjectTokenType")
+ .setTokenUrl(transportFactory.transport.getStsUrl())
+ .setCredentialSource(buildCredentialSource())
+ .setExecutableHandler(options -> "pluggableAuthToken")
+ .build();
+
+ credentials.refresh();
+ TrustBoundary trustBoundary = credentials.getTrustBoundary();
+ assertNotNull(trustBoundary);
+ assertEquals(TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION, trustBoundary.getEncodedLocations());
+ TrustBoundary.setEnvironmentProviderForTest(null);
+ }
+
private static PluggableAuthCredentialSource buildCredentialSource() {
return buildCredentialSource("command", null, null);
}
diff --git a/oauth2_http/javatests/com/google/auth/oauth2/ServiceAccountCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/ServiceAccountCredentialsTest.java
index 1407f024f..ca275f1e3 100644
--- a/oauth2_http/javatests/com/google/auth/oauth2/ServiceAccountCredentialsTest.java
+++ b/oauth2_http/javatests/com/google/auth/oauth2/ServiceAccountCredentialsTest.java
@@ -31,6 +31,7 @@
package com.google.auth.oauth2;
+import static com.google.auth.oauth2.TrustBoundary.TRUST_BOUNDARY_KEY;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -1816,15 +1817,16 @@ public void refresh_trustBoundarySuccess() throws IOException {
// Mock trust boundary response
TrustBoundary trustBoundary =
- new TrustBoundary("0x80000", Collections.singletonList("us-central1"));
+ new TrustBoundary(
+ TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION, TestUtils.TRUST_BOUNDARY_LOCATIONS);
MockTokenServerTransport transport = new MockTokenServerTransport();
- transport.addServiceAccount("test-client-email@example.com", "test-access-token");
+ transport.addServiceAccount(CLIENT_EMAIL, "test-access-token");
transport.setTrustBoundary(trustBoundary);
ServiceAccountCredentials credentials =
ServiceAccountCredentials.newBuilder()
- .setClientEmail("test-client-email@example.com")
+ .setClientEmail(CLIENT_EMAIL)
.setPrivateKey(
OAuth2Utils.privateKeyFromPkcs8(ServiceAccountCredentialsTest.PRIVATE_KEY_PKCS8))
.setPrivateKeyId("test-key-id")
@@ -1833,7 +1835,8 @@ public void refresh_trustBoundarySuccess() throws IOException {
.build();
Map> headers = credentials.getRequestMetadata();
- assertEquals(headers.get("x-allowed-locations"), Arrays.asList("0x80000"));
+ assertEquals(
+ headers.get(TRUST_BOUNDARY_KEY), Arrays.asList(TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION));
}
@Test
@@ -1843,11 +1846,11 @@ public void refresh_trustBoundaryFails_throwsIOException() throws IOException {
environmentProvider.setEnv("GOOGLE_AUTH_TRUST_BOUNDARY_ENABLE_EXPERIMENT", "1");
MockTokenServerTransport transport = new MockTokenServerTransport();
- transport.addServiceAccount("test-client-email@example.com", "test-access-token");
+ transport.addServiceAccount(CLIENT_EMAIL, "test-access-token");
ServiceAccountCredentials credentials =
ServiceAccountCredentials.newBuilder()
- .setClientEmail("test-client-email@example.com")
+ .setClientEmail(CLIENT_EMAIL)
.setPrivateKey(
OAuth2Utils.privateKeyFromPkcs8(ServiceAccountCredentialsTest.PRIVATE_KEY_PKCS8))
.setPrivateKeyId("test-key-id")