-
Notifications
You must be signed in to change notification settings - Fork 1.2k
feat: Enable Bound Token for Agentic Identities #13873
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: agentic-identities-bound-token
Are you sure you want to change the base?
Changes from all commits
0af6032
9d5672f
a591db4
18afacb
7a2ae21
2c1c298
0e3d440
c5fab1d
0036d0c
4d99d25
727b596
bd2f98b
e3d105e
cb71972
a4b6192
71cd2c1
ea07ce4
5b1c82b
0649e49
6181f6a
a62afa4
7742a79
09da90a
b95a5da
5c444d1
618a317
fb2ba53
c951bbe
f7dedb9
0d6d702
27776f6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -34,11 +34,15 @@ | |
| import static com.google.common.base.MoreObjects.firstNonNull; | ||
|
|
||
| import com.google.api.client.http.GenericUrl; | ||
| import com.google.api.client.http.HttpContent; | ||
| import com.google.api.client.http.HttpHeaders; | ||
| import com.google.api.client.http.HttpMediaType; | ||
| import com.google.api.client.http.HttpRequest; | ||
| import com.google.api.client.http.HttpRequestFactory; | ||
| import com.google.api.client.http.HttpResponse; | ||
| import com.google.api.client.http.HttpResponseException; | ||
| import com.google.api.client.http.HttpStatusCodes; | ||
| import com.google.api.client.http.json.JsonHttpContent; | ||
| import com.google.api.client.json.JsonObjectParser; | ||
| import com.google.api.client.util.GenericData; | ||
| import com.google.auth.CredentialTypeForMetrics; | ||
|
|
@@ -68,6 +72,7 @@ | |
| 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; | ||
|
|
@@ -320,7 +325,7 @@ public String getUniverseDomain() throws IOException { | |
|
|
||
| private String getUniverseDomainFromMetadata() throws IOException { | ||
| HttpResponse response = | ||
| getMetadataResponse(getUniverseDomainUrl(), RequestType.UNTRACKED, false); | ||
| getMetadataResponse(getUniverseDomainUrl(), "GET", null, RequestType.UNTRACKED, false); | ||
| int statusCode = response.getStatusCode(); | ||
| if (statusCode == HttpStatusCodes.STATUS_CODE_NOT_FOUND) { | ||
| return Credentials.GOOGLE_DEFAULT_UNIVERSE; | ||
|
|
@@ -379,7 +384,8 @@ public String getProjectId() { | |
|
|
||
| private String getProjectIdFromMetadata() { | ||
| try { | ||
| HttpResponse response = getMetadataResponse(getProjectIdUrl(), RequestType.UNTRACKED, false); | ||
| HttpResponse response = | ||
| getMetadataResponse(getProjectIdUrl(), "GET", null, RequestType.UNTRACKED, false); | ||
| int statusCode = response.getStatusCode(); | ||
| if (statusCode == HttpStatusCodes.STATUS_CODE_NOT_FOUND) { | ||
| LoggingUtils.log( | ||
|
|
@@ -421,17 +427,17 @@ private String getProjectIdFromMetadata() { | |
| /** Refresh the access token by getting it from the GCE metadata server */ | ||
| @Override | ||
| public AccessToken refreshAccessToken() throws IOException { | ||
| String tokenUrl = createTokenUrlWithScopes(); | ||
| HttpResponse response = | ||
| getMetadataResponse(createTokenUrlWithScopes(), RequestType.ACCESS_TOKEN_REQUEST, true); | ||
| getMetadataResponseForToken(tokenUrl, RequestType.ACCESS_TOKEN_REQUEST, true); | ||
| int statusCode = response.getStatusCode(); | ||
| if (statusCode == HttpStatusCodes.STATUS_CODE_NOT_FOUND) { | ||
| throw new IOException( | ||
| String.format( | ||
| "Error code %s trying to get security access token from" | ||
| + " Compute Engine metadata for the default service account. This may be because" | ||
| + " the virtual machine instance does not have permission scopes specified." | ||
| + " It is possible to skip checking for Compute Engine metadata by specifying the environment " | ||
| + " variable " | ||
| "Error code %s trying to get security access token from Compute Engine metadata for" | ||
| + " the default service account. This may be because the virtual machine instance" | ||
| + " does not have permission scopes specified. It is possible to skip checking" | ||
| + " for Compute Engine metadata by specifying the environment variable " | ||
| + DefaultCredentialsProvider.NO_GCE_CHECK_ENV_VAR | ||
| + "=true.", | ||
| statusCode)); | ||
|
|
@@ -491,7 +497,7 @@ public IdToken idTokenWithAudience(String targetAudience, List<IdTokenProvider.O | |
| } | ||
| documentUrl.set("audience", targetAudience); | ||
| HttpResponse response = | ||
| getMetadataResponse(documentUrl.toString(), RequestType.ID_TOKEN_REQUEST, true); | ||
| getMetadataResponseForToken(documentUrl.toString(), RequestType.ID_TOKEN_REQUEST, true); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bound ID token POST requests don't pass |
||
| int statusCode = response.getStatusCode(); | ||
| if (statusCode == HttpStatusCodes.STATUS_CODE_NOT_FOUND) { | ||
| throw new IOException( | ||
|
|
@@ -504,7 +510,8 @@ public IdToken idTokenWithAudience(String targetAudience, List<IdTokenProvider.O | |
| if (statusCode != HttpStatusCodes.STATUS_CODE_OK) { | ||
| throw new IOException( | ||
| String.format( | ||
| "Unexpected Error code %s trying to get identity token from Compute Engine metadata: %s", | ||
| "Unexpected Error code %s trying to get identity token from Compute Engine metadata:" | ||
| + " %s", | ||
| statusCode, response.parseAsString())); | ||
| } | ||
| InputStream content = response.getContent(); | ||
|
|
@@ -519,11 +526,35 @@ public IdToken idTokenWithAudience(String targetAudience, List<IdTokenProvider.O | |
| return IdToken.create(rawToken); | ||
| } | ||
|
|
||
| private HttpResponse getMetadataResponse( | ||
| private HttpResponse getMetadataResponseForToken( | ||
| String url, RequestType requestType, boolean shouldSendMetricsHeader) throws IOException { | ||
| String boundTokenPayload = AgentIdentityUtils.getBoundTokenPayload(); | ||
| if (boundTokenPayload != null) { | ||
| Map<String, String> payload = | ||
| Collections.singletonMap("certificate_chain", boundTokenPayload); | ||
| HttpContent content = | ||
| new JsonHttpContent(OAuth2Utils.JSON_FACTORY, payload) | ||
| .setMediaType(new HttpMediaType("application/json")); | ||
| return getMetadataResponse(url, "POST", content, requestType, shouldSendMetricsHeader); | ||
| } | ||
| return getMetadataResponse(url, "GET", null, requestType, shouldSendMetricsHeader); | ||
| } | ||
|
|
||
| private HttpResponse getMetadataResponse( | ||
| String url, | ||
| String method, | ||
| @Nullable HttpContent content, | ||
| RequestType requestType, | ||
| boolean shouldSendMetricsHeader) | ||
| throws IOException { | ||
| GenericUrl genericUrl = new GenericUrl(url); | ||
| HttpRequest request = | ||
| transportFactory.create().createRequestFactory().buildGetRequest(genericUrl); | ||
| HttpRequestFactory requestFactory = transportFactory.create().createRequestFactory(); | ||
| HttpRequest request; | ||
| if ("POST".equals(method)) { | ||
| request = requestFactory.buildPostRequest(genericUrl, content); | ||
| } else { | ||
| request = requestFactory.buildGetRequest(genericUrl); | ||
| } | ||
| // Disable automatic logging by google-http-java-client to prevent leakage of sensitive tokens. | ||
| // Client Library Debug Logging via LoggingUtils is used instead where appropriate. | ||
| request.setLoggingEnabled(false); | ||
|
|
@@ -840,7 +871,8 @@ public byte[] sign(byte[] toSign) { | |
|
|
||
| private String getDefaultServiceAccount() throws IOException { | ||
| HttpResponse response = | ||
| getMetadataResponse(getDefaultServiceAccountUrl(), RequestType.UNTRACKED, false); | ||
| getMetadataResponse( | ||
| getDefaultServiceAccountUrl(), "GET", null, RequestType.UNTRACKED, false); | ||
| int statusCode = response.getStatusCode(); | ||
| if (statusCode == HttpStatusCodes.STATUS_CODE_NOT_FOUND) { | ||
| throw new IOException( | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Making
PEM_CERT_PATTERNpackage-private couplesAgentIdentityUtilsto this class. Should we move the shared PEM regex toOAuth2Utilsinstead?