Skip to content

Commit f1f306d

Browse files
authored
fix: handle optional fields in ExternalAccountCredentials with null JSON value gracefully (#1706)
1 parent 47ba77f commit f1f306d

File tree

2 files changed

+35
-8
lines changed

2 files changed

+35
-8
lines changed

oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import com.google.api.client.http.HttpHeaders;
3737
import com.google.api.client.json.GenericJson;
3838
import com.google.api.client.json.JsonObjectParser;
39+
import com.google.api.client.util.Data;
3940
import com.google.auth.RequestMetadataCallback;
4041
import com.google.auth.http.HttpTransportFactory;
4142
import com.google.common.base.MoreObjects;
@@ -418,15 +419,16 @@ static ExternalAccountCredentials fromJson(
418419
Map<String, Object> credentialSourceMap = (Map<String, Object>) json.get("credential_source");
419420

420421
// Optional params.
421-
String serviceAccountImpersonationUrl = (String) json.get("service_account_impersonation_url");
422-
String tokenInfoUrl = (String) json.get("token_info_url");
423-
String clientId = (String) json.get("client_id");
424-
String clientSecret = (String) json.get("client_secret");
425-
String quotaProjectId = (String) json.get("quota_project_id");
426-
String userProject = (String) json.get("workforce_pool_user_project");
427-
String universeDomain = (String) json.get("universe_domain");
422+
String serviceAccountImpersonationUrl =
423+
getOptional(json, "service_account_impersonation_url", String.class);
424+
String tokenInfoUrl = getOptional(json, "token_info_url", String.class);
425+
String clientId = getOptional(json, "client_id", String.class);
426+
String clientSecret = getOptional(json, "client_secret", String.class);
427+
String quotaProjectId = getOptional(json, "quota_project_id", String.class);
428+
String userProject = getOptional(json, "workforce_pool_user_project", String.class);
429+
String universeDomain = getOptional(json, "universe_domain", String.class);
428430
Map<String, Object> impersonationOptionsMap =
429-
(Map<String, Object>) json.get("service_account_impersonation");
431+
getOptional(json, "service_account_impersonation", Map.class);
430432

431433
if (impersonationOptionsMap == null) {
432434
impersonationOptionsMap = new HashMap<String, Object>();
@@ -481,6 +483,14 @@ static ExternalAccountCredentials fromJson(
481483
.build();
482484
}
483485

486+
private static <T> T getOptional(Map<String, Object> json, String fieldName, Class<T> clazz) {
487+
Object value = json.get(fieldName);
488+
if (value == null || Data.isNull(value)) {
489+
return null;
490+
}
491+
return clazz.cast(value);
492+
}
493+
484494
private static boolean isPluggableAuthCredential(Map<String, Object> credentialSource) {
485495
// Pluggable Auth is enabled via a nested executable field in the credential source.
486496
return credentialSource.containsKey(EXECUTABLE_SOURCE_KEY);

oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountCredentialsTest.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,23 @@ public void fromStream_nullTransport_throws() throws IOException {
148148
}
149149
}
150150

151+
@Test
152+
public void fromStream_nullOptionalField() throws IOException {
153+
ExternalAccountCredentials credentials =
154+
ExternalAccountCredentials.fromStream(
155+
new ByteArrayInputStream(
156+
("{"
157+
+ "\"service_account_impersonation_url\": null,"
158+
+
159+
// required
160+
"\"audience\": \"audience\","
161+
+ "\"subject_token_type\": \"subjectTokenType\","
162+
+ "\"credential_source\": {\"file\":\"file\"}"
163+
+ "}")
164+
.getBytes()));
165+
assertNull(credentials.getServiceAccountImpersonationUrl());
166+
}
167+
151168
@Test
152169
public void fromStream_nullStream_throws() throws IOException {
153170
try {

0 commit comments

Comments
 (0)