Skip to content

Commit 8b9072e

Browse files
committed
fix(auth): defer certificate-bound ID tokens; bind access tokens only (#13873)
For the initial launch, only access tokens are certificate-bound for agent identities. ComputeEngineCredentials.idTokenWithAudience() goes back to a plain GET of the identity endpoint: no certificate_chain POST body, no forced format=full, and no bound-token-specific 404 message. This matches Google Auth Library for Python, which binds access tokens but not ID tokens. refreshAccessToken() is unchanged and still requests bound access tokens. Tests: the ID-token cases in ComputeEngineCredentialsTest now assert that, with an agent identity certificate present and binding enabled, ID tokens are requested unbound, and that a 404 produces the standard ID-token error.
1 parent f17f773 commit 8b9072e

2 files changed

Lines changed: 15 additions & 29 deletions

File tree

‎google-auth-library-java/oauth2_http/java/com/google/auth/oauth2/ComputeEngineCredentials.java‎

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -494,11 +494,9 @@ public AccessToken refreshAccessToken() throws IOException {
494494
@Override
495495
public IdToken idTokenWithAudience(String targetAudience, List<IdTokenProvider.Option> options)
496496
throws IOException {
497-
String boundTokenPayload = AgentIdentityUtils.getBoundTokenPayload();
497+
// Certificate-bound ID tokens are intentionally not requested yet; only access tokens are
498+
// bound for agent identities (see refreshAccessToken). ID tokens are always fetched unbound.
498499
GenericUrl documentUrl = new GenericUrl(getIdentityDocumentUrl());
499-
if (boundTokenPayload != null) {
500-
documentUrl.set("format", "full");
501-
}
502500
if (options != null) {
503501
if (options.contains(IdTokenProvider.Option.FORMAT_FULL)) {
504502
documentUrl.set("format", "full");
@@ -511,18 +509,10 @@ public IdToken idTokenWithAudience(String targetAudience, List<IdTokenProvider.O
511509
}
512510
documentUrl.set("audience", targetAudience);
513511
HttpResponse response =
514-
getMetadataResponseForToken(
515-
documentUrl.toString(), boundTokenPayload, RequestType.ID_TOKEN_REQUEST, true);
512+
getMetadataResponse(
513+
documentUrl.toString(), "GET", null, RequestType.ID_TOKEN_REQUEST, true);
516514
int statusCode = response.getStatusCode();
517515
if (statusCode == HttpStatusCodes.STATUS_CODE_NOT_FOUND) {
518-
if (boundTokenPayload != null) {
519-
throw new IOException(
520-
String.format(
521-
"Error code %s trying to get bound identity token from Compute Engine metadata."
522-
+ " The Compute Engine metadata server endpoint does not support bound"
523-
+ " tokens.",
524-
statusCode));
525-
}
526516
throw new IOException(
527517
String.format(
528518
"Error code %s trying to get identity token from"

‎google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/ComputeEngineCredentialsTest.java‎

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1360,10 +1360,12 @@ void refreshAccessToken_withValidCertAndKey_requestsBoundToken() throws IOExcept
13601360
}
13611361

13621362
@Test
1363-
void idTokenWithAudience_withValidCertAndKey_requestsBoundToken() throws IOException {
1363+
void idTokenWithAudience_withValidCertAndKey_requestsUnboundToken() throws IOException {
1364+
// Bound ID tokens are deferred: even with an agent identity certificate present and token
1365+
// binding enabled, ID tokens are requested unbound (GET, no certificate chain, no forced
1366+
// format=full). Access tokens are still bound (see refreshAccessToken test above).
13641367
setupCertAndKeyConfig();
1365-
envProvider.setEnv(
1366-
AgentIdentityUtils.GOOGLE_API_ENABLE_RUNTIME_BOUND_TOKEN, "true"); // Enable bound token
1368+
envProvider.setEnv(AgentIdentityUtils.GOOGLE_API_ENABLE_RUNTIME_BOUND_TOKEN, "true");
13671369
MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory();
13681370
transportFactory.transport.setServiceAccountEmail(SA_CLIENT_EMAIL);
13691371
transportFactory.transport.setIdToken(STANDARD_ID_TOKEN);
@@ -1374,16 +1376,10 @@ void idTokenWithAudience_withValidCertAndKey_requestsBoundToken() throws IOExcep
13741376

13751377
assertNotNull(token);
13761378
MockLowLevelHttpRequest request = transportFactory.transport.getRequest();
1377-
assertEquals("POST", transportFactory.transport.getRequestMethod());
1378-
assertEquals("application/json", request.getContentType());
1379+
assertEquals("GET", transportFactory.transport.getRequestMethod());
13791380
assertTrue(request.getUrl().contains("audience=https://foo.bar"));
1380-
assertTrue(request.getUrl().contains("format=full"));
1381-
String body = request.getContentAsString();
1382-
GenericJson bodyJson = OAuth2Utils.JSON_FACTORY.fromString(body, GenericJson.class);
1383-
String expectedCert =
1384-
new String(Files.readAllBytes(tempDir.resolve("certificates.pem")), StandardCharsets.UTF_8)
1385-
.trim();
1386-
assertEquals(expectedCert, ((String) bodyJson.get("certificate_chain")).trim());
1381+
assertFalse(request.getUrl().contains("format=full"));
1382+
assertNull(request.getStreamingContent());
13871383
}
13881384

13891385
@Test
@@ -1401,8 +1397,7 @@ void refreshAccessToken_boundToken404_throwsEndpointDoesNotSupportBoundTokensMes
14011397
}
14021398

14031399
@Test
1404-
void idTokenWithAudience_boundToken404_throwsEndpointDoesNotSupportBoundTokensMessage()
1405-
throws IOException {
1400+
void idTokenWithAudience_withValidCertAndKey404_throwsUnboundIdTokenMessage() throws IOException {
14061401
setupCertAndKeyConfig();
14071402
envProvider.setEnv(AgentIdentityUtils.GOOGLE_API_ENABLE_RUNTIME_BOUND_TOKEN, "true");
14081403
MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory();
@@ -1413,7 +1408,8 @@ void idTokenWithAudience_boundToken404_throwsEndpointDoesNotSupportBoundTokensMe
14131408
IOException e =
14141409
assertThrows(
14151410
IOException.class, () -> credentials.idTokenWithAudience("https://foo.bar", null));
1416-
assertTrue(e.getMessage().contains("does not support bound tokens"));
1411+
assertTrue(e.getMessage().contains("trying to get identity token"));
1412+
assertFalse(e.getMessage().contains("bound"));
14171413
}
14181414

14191415
static class MockMetadataServerTransportFactory implements HttpTransportFactory {

0 commit comments

Comments
 (0)