Skip to content

Commit 919ae32

Browse files
authored
feat: support ability to set universe domain in ServiceAccountJwtAccessCredentials (#1754)
* feat: support ability to set universe domain in ServiceAccountJwtAccessCredentials.
1 parent 5fca95b commit 919ae32

File tree

2 files changed

+212
-8
lines changed

2 files changed

+212
-8
lines changed

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

Lines changed: 72 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ public class ServiceAccountJwtAccessCredentials extends Credentials
8888
private final String privateKeyId;
8989
private final URI defaultAudience;
9090
private final String quotaProjectId;
91+
private final String universeDomain;
9192

9293
private transient LoadingCache<JwtClaims, JwtCredentials> credentialsCache;
9394

@@ -104,7 +105,14 @@ public class ServiceAccountJwtAccessCredentials extends Credentials
104105
*/
105106
private ServiceAccountJwtAccessCredentials(
106107
String clientId, String clientEmail, PrivateKey privateKey, String privateKeyId) {
107-
this(clientId, clientEmail, privateKey, privateKeyId, null, null);
108+
this(
109+
clientId,
110+
clientEmail,
111+
privateKey,
112+
privateKeyId,
113+
null,
114+
null,
115+
Credentials.GOOGLE_DEFAULT_UNIVERSE);
108116
}
109117

110118
/**
@@ -115,21 +123,29 @@ private ServiceAccountJwtAccessCredentials(
115123
* @param privateKey RSA private key object for the service account.
116124
* @param privateKeyId Private key identifier for the service account. May be null.
117125
* @param defaultAudience Audience to use if not provided by transport. May be null.
126+
* @param universeDomain universe domain in the format some-domain.xyz. By default, sets it to
127+
* googleapis.com
118128
*/
119129
private ServiceAccountJwtAccessCredentials(
120130
String clientId,
121131
String clientEmail,
122132
PrivateKey privateKey,
123133
String privateKeyId,
124134
URI defaultAudience,
125-
String quotaProjectId) {
135+
String quotaProjectId,
136+
String universeDomain) {
126137
this.clientId = clientId;
127138
this.clientEmail = Preconditions.checkNotNull(clientEmail);
128139
this.privateKey = Preconditions.checkNotNull(privateKey);
129140
this.privateKeyId = privateKeyId;
130141
this.defaultAudience = defaultAudience;
131142
this.credentialsCache = createCache();
132143
this.quotaProjectId = quotaProjectId;
144+
if (universeDomain == null || universeDomain.trim().isEmpty()) {
145+
this.universeDomain = Credentials.GOOGLE_DEFAULT_UNIVERSE;
146+
} else {
147+
this.universeDomain = universeDomain;
148+
}
133149
}
134150

135151
/**
@@ -160,6 +176,10 @@ static ServiceAccountJwtAccessCredentials fromJson(Map<String, Object> json, URI
160176
String privateKeyPkcs8 = (String) json.get("private_key");
161177
String privateKeyId = (String) json.get("private_key_id");
162178
String quoataProjectId = (String) json.get("quota_project_id");
179+
String rawUniverseDomain = (String) json.get("universe_domain");
180+
String resolvedUniverseDomain =
181+
(rawUniverseDomain == null) ? Credentials.GOOGLE_DEFAULT_UNIVERSE : rawUniverseDomain;
182+
163183
if (clientId == null
164184
|| clientEmail == null
165185
|| privateKeyPkcs8 == null
@@ -169,7 +189,13 @@ static ServiceAccountJwtAccessCredentials fromJson(Map<String, Object> json, URI
169189
+ "expecting 'client_id', 'client_email', 'private_key' and 'private_key_id'.");
170190
}
171191
return ServiceAccountJwtAccessCredentials.fromPkcs8(
172-
clientId, clientEmail, privateKeyPkcs8, privateKeyId, defaultAudience, quoataProjectId);
192+
clientId,
193+
clientEmail,
194+
privateKeyPkcs8,
195+
privateKeyId,
196+
defaultAudience,
197+
quoataProjectId,
198+
resolvedUniverseDomain);
173199
}
174200

175201
/**
@@ -207,7 +233,13 @@ public static ServiceAccountJwtAccessCredentials fromPkcs8(
207233
URI defaultAudience)
208234
throws IOException {
209235
return ServiceAccountJwtAccessCredentials.fromPkcs8(
210-
clientId, clientEmail, privateKeyPkcs8, privateKeyId, defaultAudience, null);
236+
clientId,
237+
clientEmail,
238+
privateKeyPkcs8,
239+
privateKeyId,
240+
defaultAudience,
241+
null,
242+
Credentials.GOOGLE_DEFAULT_UNIVERSE);
211243
}
212244

213245
static ServiceAccountJwtAccessCredentials fromPkcs8(
@@ -216,11 +248,18 @@ static ServiceAccountJwtAccessCredentials fromPkcs8(
216248
String privateKeyPkcs8,
217249
String privateKeyId,
218250
URI defaultAudience,
219-
String quotaProjectId)
251+
String quotaProjectId,
252+
String universeDomain)
220253
throws IOException {
221254
PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(privateKeyPkcs8);
222255
return new ServiceAccountJwtAccessCredentials(
223-
clientId, clientEmail, privateKey, privateKeyId, defaultAudience, quotaProjectId);
256+
clientId,
257+
clientEmail,
258+
privateKey,
259+
privateKeyId,
260+
defaultAudience,
261+
quotaProjectId,
262+
universeDomain);
224263
}
225264

226265
/**
@@ -352,7 +391,6 @@ public Map<String, List<String>> getRequestMetadata(URI uri) throws IOException
352391
+ "defaultAudience to be specified");
353392
}
354393
}
355-
356394
try {
357395
JwtClaims defaultClaims =
358396
JwtClaims.newBuilder()
@@ -399,6 +437,12 @@ public final String getPrivateKeyId() {
399437
return privateKeyId;
400438
}
401439

440+
/** Returns the universe domain (example, googleapis.com) for the credentials instance. */
441+
@Override
442+
public final String getUniverseDomain() {
443+
return universeDomain;
444+
}
445+
402446
@Override
403447
public String getAccount() {
404448
return getClientEmail();
@@ -474,6 +518,7 @@ public static class Builder {
474518
private String privateKeyId;
475519
private URI defaultAudience;
476520
private String quotaProjectId;
521+
private String universeDomain;
477522

478523
protected Builder() {}
479524

@@ -484,6 +529,7 @@ protected Builder(ServiceAccountJwtAccessCredentials credentials) {
484529
this.privateKeyId = credentials.privateKeyId;
485530
this.defaultAudience = credentials.defaultAudience;
486531
this.quotaProjectId = credentials.quotaProjectId;
532+
this.universeDomain = credentials.universeDomain;
487533
}
488534

489535
@CanIgnoreReturnValue
@@ -522,6 +568,13 @@ public Builder setQuotaProjectId(String quotaProjectId) {
522568
return this;
523569
}
524570

571+
@CanIgnoreReturnValue
572+
/** Sets the universe domain (example, googleapis.com). */
573+
public Builder setUniverseDomain(String universeDomain) {
574+
this.universeDomain = universeDomain;
575+
return this;
576+
}
577+
525578
public String getClientId() {
526579
return clientId;
527580
}
@@ -546,9 +599,20 @@ public String getQuotaProjectId() {
546599
return quotaProjectId;
547600
}
548601

602+
/** Returns the universe domain (example, googleapis.com) for the credentials instance. */
603+
public String getUniverseDomain() {
604+
return universeDomain;
605+
}
606+
549607
public ServiceAccountJwtAccessCredentials build() {
550608
return new ServiceAccountJwtAccessCredentials(
551-
clientId, clientEmail, privateKey, privateKeyId, defaultAudience, quotaProjectId);
609+
clientId,
610+
clientEmail,
611+
privateKey,
612+
privateKeyId,
613+
defaultAudience,
614+
quotaProjectId,
615+
universeDomain);
552616
}
553617
}
554618
}

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

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131

3232
package com.google.auth.oauth2;
3333

34+
import static com.google.auth.Credentials.GOOGLE_DEFAULT_UNIVERSE;
3435
import static org.junit.Assert.assertArrayEquals;
3536
import static org.junit.Assert.assertEquals;
3637
import static org.junit.Assert.assertFalse;
@@ -41,6 +42,7 @@
4142
import static org.junit.Assert.assertTrue;
4243
import static org.junit.Assert.fail;
4344

45+
import com.google.api.client.json.GenericJson;
4446
import com.google.api.client.json.JsonFactory;
4547
import com.google.api.client.json.gson.GsonFactory;
4648
import com.google.api.client.json.webtoken.JsonWebSignature;
@@ -111,6 +113,7 @@ public void constructor_allParameters_constructs() throws IOException {
111113
assertEquals(privateKey, credentials.getPrivateKey());
112114
assertEquals(SA_PRIVATE_KEY_ID, credentials.getPrivateKeyId());
113115
assertEquals(QUOTA_PROJECT, credentials.getQuotaProjectId());
116+
assertEquals(Credentials.GOOGLE_DEFAULT_UNIVERSE, credentials.getUniverseDomain());
114117
}
115118

116119
@Test
@@ -829,6 +832,109 @@ public void onFailure(Throwable exception) {
829832
assertTrue("Should have run onSuccess() callback", success.get());
830833
}
831834

835+
@Test
836+
public void fromJSON_noUniverseDomain() throws IOException {
837+
GenericJson json =
838+
writeServiceAccountJson(
839+
SA_CLIENT_ID,
840+
SA_CLIENT_EMAIL,
841+
SA_PRIVATE_KEY_PKCS8,
842+
"test-project-id",
843+
SA_PRIVATE_KEY_ID,
844+
QUOTA_PROJECT,
845+
null);
846+
ServiceAccountJwtAccessCredentials credentials =
847+
ServiceAccountJwtAccessCredentials.fromJson(json, URI.create("default-aud"));
848+
assertEquals(SA_CLIENT_ID, credentials.getClientId());
849+
assertEquals(SA_CLIENT_EMAIL, credentials.getClientEmail());
850+
assertEquals(
851+
OAuth2Utils.privateKeyFromPkcs8(SA_PRIVATE_KEY_PKCS8), credentials.getPrivateKey());
852+
assertEquals(QUOTA_PROJECT, credentials.getQuotaProjectId());
853+
assertEquals(GOOGLE_DEFAULT_UNIVERSE, credentials.getUniverseDomain());
854+
}
855+
856+
@Test
857+
public void fromJSON_UniverseDomainSet() throws IOException {
858+
GenericJson json =
859+
writeServiceAccountJson(
860+
SA_CLIENT_ID,
861+
SA_CLIENT_EMAIL,
862+
SA_PRIVATE_KEY_PKCS8,
863+
"test-project-id",
864+
SA_PRIVATE_KEY_ID,
865+
QUOTA_PROJECT,
866+
"example.com");
867+
ServiceAccountJwtAccessCredentials credentials =
868+
ServiceAccountJwtAccessCredentials.fromJson(json, URI.create("default-aud"));
869+
assertEquals(SA_CLIENT_ID, credentials.getClientId());
870+
assertEquals(SA_CLIENT_EMAIL, credentials.getClientEmail());
871+
assertEquals(
872+
OAuth2Utils.privateKeyFromPkcs8(SA_PRIVATE_KEY_PKCS8), credentials.getPrivateKey());
873+
assertEquals(QUOTA_PROJECT, credentials.getQuotaProjectId());
874+
assertEquals("example.com", credentials.getUniverseDomain());
875+
}
876+
877+
@Test
878+
public void fromPkcs8_NoUniverseDomain() throws IOException {
879+
ServiceAccountJwtAccessCredentials credentials =
880+
ServiceAccountJwtAccessCredentials.fromPkcs8(
881+
SA_CLIENT_ID, SA_CLIENT_EMAIL, SA_PRIVATE_KEY_PKCS8, SA_PRIVATE_KEY_ID);
882+
assertEquals(SA_CLIENT_ID, credentials.getClientId());
883+
assertEquals(SA_CLIENT_EMAIL, credentials.getClientEmail());
884+
assertEquals(
885+
OAuth2Utils.privateKeyFromPkcs8(SA_PRIVATE_KEY_PKCS8), credentials.getPrivateKey());
886+
assertNull(credentials.getQuotaProjectId());
887+
assertEquals(Credentials.GOOGLE_DEFAULT_UNIVERSE, credentials.getUniverseDomain());
888+
}
889+
890+
@Test
891+
public void fromPkcs8_CustomUniverseDomain() throws IOException {
892+
ServiceAccountJwtAccessCredentials credentials =
893+
ServiceAccountJwtAccessCredentials.fromPkcs8(
894+
SA_CLIENT_ID,
895+
SA_CLIENT_EMAIL,
896+
SA_PRIVATE_KEY_PKCS8,
897+
SA_PRIVATE_KEY_ID,
898+
URI.create("default-aud"),
899+
QUOTA_PROJECT,
900+
"example.com");
901+
assertEquals(SA_CLIENT_ID, credentials.getClientId());
902+
assertEquals(SA_CLIENT_EMAIL, credentials.getClientEmail());
903+
assertEquals(
904+
OAuth2Utils.privateKeyFromPkcs8(SA_PRIVATE_KEY_PKCS8), credentials.getPrivateKey());
905+
assertEquals(QUOTA_PROJECT, credentials.getQuotaProjectId());
906+
assertEquals("example.com", credentials.getUniverseDomain());
907+
}
908+
909+
@Test
910+
public void builder_defaultUniverseDomain() throws IOException {
911+
PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(SA_PRIVATE_KEY_PKCS8);
912+
ServiceAccountJwtAccessCredentials credentials =
913+
ServiceAccountJwtAccessCredentials.newBuilder()
914+
.setClientId(SA_CLIENT_ID)
915+
.setClientEmail(SA_CLIENT_EMAIL)
916+
.setPrivateKey(privateKey)
917+
.setPrivateKeyId(SA_PRIVATE_KEY_ID)
918+
.setDefaultAudience(URI.create("default-audience"))
919+
.build();
920+
assertEquals(Credentials.GOOGLE_DEFAULT_UNIVERSE, credentials.getUniverseDomain());
921+
}
922+
923+
@Test
924+
public void builder_customUniverseDomain() throws IOException {
925+
PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(SA_PRIVATE_KEY_PKCS8);
926+
ServiceAccountJwtAccessCredentials credentials =
927+
ServiceAccountJwtAccessCredentials.newBuilder()
928+
.setClientId(SA_CLIENT_ID)
929+
.setClientEmail(SA_CLIENT_EMAIL)
930+
.setPrivateKey(privateKey)
931+
.setPrivateKeyId(SA_PRIVATE_KEY_ID)
932+
.setDefaultAudience(URI.create("default-audience"))
933+
.setUniverseDomain("example.com")
934+
.build();
935+
assertEquals("example.com", credentials.getUniverseDomain());
936+
}
937+
832938
private void verifyJwtAccess(
833939
Map<String, List<String>> metadata,
834940
String expectedEmail,
@@ -863,4 +969,38 @@ private static void testFromStreamException(InputStream stream, String expectedM
863969
assertTrue(expected.getMessage().contains(expectedMessageContent));
864970
}
865971
}
972+
973+
private GenericJson writeServiceAccountJson(
974+
String clientId,
975+
String clientEmail,
976+
String privateKeyPkcs8,
977+
String privateKeyId,
978+
String projectId,
979+
String quotaProjectId,
980+
String universeDomain) {
981+
GenericJson json = new GenericJson();
982+
if (clientId != null) {
983+
json.put("client_id", clientId);
984+
}
985+
if (clientEmail != null) {
986+
json.put("client_email", clientEmail);
987+
}
988+
if (privateKeyPkcs8 != null) {
989+
json.put("private_key", privateKeyPkcs8);
990+
}
991+
if (privateKeyId != null) {
992+
json.put("private_key_id", privateKeyId);
993+
}
994+
if (projectId != null) {
995+
json.put("project_id", projectId);
996+
}
997+
if (quotaProjectId != null) {
998+
json.put("quota_project_id", quotaProjectId);
999+
}
1000+
if (universeDomain != null) {
1001+
json.put("universe_domain", universeDomain);
1002+
}
1003+
json.put("type", GoogleCredentials.SERVICE_ACCOUNT_FILE_TYPE);
1004+
return json;
1005+
}
8661006
}

0 commit comments

Comments
 (0)