1 /* 2 * Copyright 2015, Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * 15 * * Neither the name of Google Inc. nor the names of its 16 * contributors may be used to endorse or promote products derived from 17 * this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 package com.google.auth.oauth2; 33 34 import static com.google.common.base.MoreObjects.firstNonNull; 35 36 import com.google.api.client.http.GenericUrl; 37 import com.google.api.client.http.HttpBackOffIOExceptionHandler; 38 import com.google.api.client.http.HttpBackOffUnsuccessfulResponseHandler; 39 import com.google.api.client.http.HttpRequest; 40 import com.google.api.client.http.HttpRequestFactory; 41 import com.google.api.client.http.HttpResponse; 42 import com.google.api.client.http.HttpResponseException; 43 import com.google.api.client.http.UrlEncodedContent; 44 import com.google.api.client.json.JsonFactory; 45 import com.google.api.client.json.JsonObjectParser; 46 import com.google.api.client.json.webtoken.JsonWebSignature; 47 import com.google.api.client.json.webtoken.JsonWebToken; 48 import com.google.api.client.util.ExponentialBackOff; 49 import com.google.api.client.util.GenericData; 50 import com.google.api.client.util.Joiner; 51 import com.google.api.client.util.Preconditions; 52 import com.google.auth.Credentials; 53 import com.google.auth.RequestMetadataCallback; 54 import com.google.auth.ServiceAccountSigner; 55 import com.google.auth.http.HttpTransportFactory; 56 import com.google.common.annotations.VisibleForTesting; 57 import com.google.common.base.MoreObjects.ToStringHelper; 58 import com.google.common.collect.ImmutableSet; 59 import com.google.errorprone.annotations.CanIgnoreReturnValue; 60 import java.io.IOException; 61 import java.io.InputStream; 62 import java.io.ObjectInputStream; 63 import java.net.URI; 64 import java.net.URISyntaxException; 65 import java.security.GeneralSecurityException; 66 import java.security.InvalidKeyException; 67 import java.security.NoSuchAlgorithmException; 68 import java.security.PrivateKey; 69 import java.security.Signature; 70 import java.security.SignatureException; 71 import java.util.Collection; 72 import java.util.Collections; 73 import java.util.Date; 74 import java.util.List; 75 import java.util.Map; 76 import java.util.Objects; 77 import java.util.concurrent.Executor; 78 79 /** 80 * OAuth2 credentials representing a Service Account for calling Google APIs. 81 * 82 * <p>By default uses a JSON Web Token (JWT) to fetch access tokens. 83 */ 84 public class ServiceAccountCredentials extends GoogleCredentials 85 implements ServiceAccountSigner, IdTokenProvider, JwtProvider { 86 87 private static final long serialVersionUID = 7807543542681217978L; 88 private static final String GRANT_TYPE = "urn:ietf:params:oauth:grant-type:jwt-bearer"; 89 private static final String PARSE_ERROR_PREFIX = "Error parsing token refresh response. "; 90 private static final int TWELVE_HOURS_IN_SECONDS = 43200; 91 private static final int DEFAULT_LIFETIME_IN_SECONDS = 3600; 92 private static final int INITIAL_RETRY_INTERVAL_MILLIS = 1000; 93 private static final double RETRY_RANDOMIZATION_FACTOR = 0.1; 94 private static final double RETRY_MULTIPLIER = 2; 95 static final int DEFAULT_NUMBER_OF_RETRIES = 3; 96 97 private final String clientId; 98 private final String clientEmail; 99 private final PrivateKey privateKey; 100 private final String privateKeyId; 101 private final String serviceAccountUser; 102 private final String projectId; 103 private final String transportFactoryClassName; 104 private final URI tokenServerUri; 105 private final Collection<String> scopes; 106 private final Collection<String> defaultScopes; 107 private final int lifetime; 108 private final boolean useJwtAccessWithScope; 109 private final boolean defaultRetriesEnabled; 110 111 private transient HttpTransportFactory transportFactory; 112 113 private transient JwtCredentials selfSignedJwtCredentialsWithScope = null; 114 115 /** 116 * Internal constructor 117 * 118 * @param builder A builder for {@link ServiceAccountCredentials} See {@link 119 * ServiceAccountCredentials.Builder} 120 */ ServiceAccountCredentials(ServiceAccountCredentials.Builder builder)121 ServiceAccountCredentials(ServiceAccountCredentials.Builder builder) { 122 super(builder); 123 this.clientId = builder.clientId; 124 this.clientEmail = Preconditions.checkNotNull(builder.clientEmail); 125 this.privateKey = Preconditions.checkNotNull(builder.privateKey); 126 this.privateKeyId = builder.privateKeyId; 127 this.scopes = 128 (builder.scopes == null) ? ImmutableSet.<String>of() : ImmutableSet.copyOf(builder.scopes); 129 this.defaultScopes = 130 (builder.defaultScopes == null) 131 ? ImmutableSet.<String>of() 132 : ImmutableSet.copyOf(builder.defaultScopes); 133 this.transportFactory = 134 firstNonNull( 135 builder.transportFactory, 136 getFromServiceLoader(HttpTransportFactory.class, OAuth2Utils.HTTP_TRANSPORT_FACTORY)); 137 this.transportFactoryClassName = this.transportFactory.getClass().getName(); 138 this.tokenServerUri = 139 (builder.tokenServerUri == null) ? OAuth2Utils.TOKEN_SERVER_URI : builder.tokenServerUri; 140 this.serviceAccountUser = builder.serviceAccountUser; 141 this.projectId = builder.projectId; 142 if (builder.lifetime > TWELVE_HOURS_IN_SECONDS) { 143 throw new IllegalStateException("lifetime must be less than or equal to 43200"); 144 } 145 this.lifetime = builder.lifetime; 146 this.useJwtAccessWithScope = builder.useJwtAccessWithScope; 147 this.defaultRetriesEnabled = builder.defaultRetriesEnabled; 148 } 149 150 /** 151 * Returns service account credentials defined by JSON using the format supported by the Google 152 * Developers Console. 153 * 154 * @param json a map from the JSON representing the credentials. 155 * @param transportFactory HTTP transport factory, creates the transport used to get access 156 * tokens. 157 * @return the credentials defined by the JSON. 158 * @throws IOException if the credential cannot be created from the JSON. 159 */ fromJson( Map<String, Object> json, HttpTransportFactory transportFactory)160 static ServiceAccountCredentials fromJson( 161 Map<String, Object> json, HttpTransportFactory transportFactory) throws IOException { 162 String clientId = (String) json.get("client_id"); 163 String clientEmail = (String) json.get("client_email"); 164 String privateKeyPkcs8 = (String) json.get("private_key"); 165 String privateKeyId = (String) json.get("private_key_id"); 166 String projectId = (String) json.get("project_id"); 167 String tokenServerUriStringFromCreds = (String) json.get("token_uri"); 168 String quotaProjectId = (String) json.get("quota_project_id"); 169 String universeDomain = (String) json.get("universe_domain"); 170 URI tokenServerUriFromCreds = null; 171 try { 172 if (tokenServerUriStringFromCreds != null) { 173 tokenServerUriFromCreds = new URI(tokenServerUriStringFromCreds); 174 } 175 } catch (URISyntaxException e) { 176 throw new IOException("Token server URI specified in 'token_uri' could not be parsed."); 177 } 178 179 if (clientId == null 180 || clientEmail == null 181 || privateKeyPkcs8 == null 182 || privateKeyId == null) { 183 throw new IOException( 184 "Error reading service account credential from JSON, " 185 + "expecting 'client_id', 'client_email', 'private_key' and 'private_key_id'."); 186 } 187 188 ServiceAccountCredentials.Builder builder = 189 ServiceAccountCredentials.newBuilder() 190 .setClientId(clientId) 191 .setClientEmail(clientEmail) 192 .setPrivateKeyId(privateKeyId) 193 .setHttpTransportFactory(transportFactory) 194 .setTokenServerUri(tokenServerUriFromCreds) 195 .setProjectId(projectId) 196 .setQuotaProjectId(quotaProjectId) 197 .setUniverseDomain(universeDomain); 198 199 return fromPkcs8(privateKeyPkcs8, builder); 200 } 201 202 /** 203 * Factory with minimum identifying information using PKCS#8 for the private key. 204 * 205 * @param clientId Client ID of the service account from the console. May be null. 206 * @param clientEmail Client email address of the service account from the console. 207 * @param privateKeyPkcs8 RSA private key object for the service account in PKCS#8 format. 208 * @param privateKeyId Private key identifier for the service account. May be null. 209 * @param scopes Scope strings for the APIs to be called. May be null or an empty collection, 210 * which results in a credential that must have createScoped called before use. 211 * @return New ServiceAccountCredentials created from a private key. 212 * @throws IOException if the credential cannot be created from the private key. 213 */ fromPkcs8( String clientId, String clientEmail, String privateKeyPkcs8, String privateKeyId, Collection<String> scopes)214 public static ServiceAccountCredentials fromPkcs8( 215 String clientId, 216 String clientEmail, 217 String privateKeyPkcs8, 218 String privateKeyId, 219 Collection<String> scopes) 220 throws IOException { 221 ServiceAccountCredentials.Builder builder = 222 ServiceAccountCredentials.newBuilder() 223 .setClientId(clientId) 224 .setClientEmail(clientEmail) 225 .setPrivateKeyId(privateKeyId) 226 .setScopes(scopes); 227 228 return fromPkcs8(privateKeyPkcs8, builder); 229 } 230 231 /** 232 * Factory with minimum identifying information using PKCS#8 for the private key. 233 * 234 * @param clientId client ID of the service account from the console. May be null. 235 * @param clientEmail client email address of the service account from the console 236 * @param privateKeyPkcs8 RSA private key object for the service account in PKCS#8 format. 237 * @param privateKeyId private key identifier for the service account. May be null. 238 * @param scopes scope strings for the APIs to be called. May be null or an empty collection. 239 * @param defaultScopes default scope strings for the APIs to be called. May be null or an empty. 240 * @return new ServiceAccountCredentials created from a private key 241 * @throws IOException if the credential cannot be created from the private key 242 */ fromPkcs8( String clientId, String clientEmail, String privateKeyPkcs8, String privateKeyId, Collection<String> scopes, Collection<String> defaultScopes)243 public static ServiceAccountCredentials fromPkcs8( 244 String clientId, 245 String clientEmail, 246 String privateKeyPkcs8, 247 String privateKeyId, 248 Collection<String> scopes, 249 Collection<String> defaultScopes) 250 throws IOException { 251 ServiceAccountCredentials.Builder builder = 252 ServiceAccountCredentials.newBuilder() 253 .setClientId(clientId) 254 .setClientEmail(clientEmail) 255 .setPrivateKeyId(privateKeyId) 256 .setScopes(scopes, defaultScopes); 257 258 return fromPkcs8(privateKeyPkcs8, builder); 259 } 260 261 /** 262 * Factory with minimum identifying information and custom transport using PKCS#8 for the private 263 * key. 264 * 265 * @param clientId Client ID of the service account from the console. May be null. 266 * @param clientEmail Client email address of the service account from the console. 267 * @param privateKeyPkcs8 RSA private key object for the service account in PKCS#8 format. 268 * @param privateKeyId Private key identifier for the service account. May be null. 269 * @param scopes Scope strings for the APIs to be called. May be null or an empty collection, 270 * which results in a credential that must have createScoped called before use. 271 * @param transportFactory HTTP transport factory, creates the transport used to get access 272 * tokens. 273 * @param tokenServerUri URI of the end point that provides tokens. 274 * @return New ServiceAccountCredentials created from a private key. 275 * @throws IOException if the credential cannot be created from the private key. 276 */ fromPkcs8( String clientId, String clientEmail, String privateKeyPkcs8, String privateKeyId, Collection<String> scopes, HttpTransportFactory transportFactory, URI tokenServerUri)277 public static ServiceAccountCredentials fromPkcs8( 278 String clientId, 279 String clientEmail, 280 String privateKeyPkcs8, 281 String privateKeyId, 282 Collection<String> scopes, 283 HttpTransportFactory transportFactory, 284 URI tokenServerUri) 285 throws IOException { 286 287 ServiceAccountCredentials.Builder builder = 288 ServiceAccountCredentials.newBuilder() 289 .setClientId(clientId) 290 .setClientEmail(clientEmail) 291 .setPrivateKeyId(privateKeyId) 292 .setScopes(scopes) 293 .setHttpTransportFactory(transportFactory) 294 .setTokenServerUri(tokenServerUri); 295 296 return fromPkcs8(privateKeyPkcs8, builder); 297 } 298 299 /** 300 * Factory with minimum identifying information and custom transport using PKCS#8 for the private 301 * key. 302 * 303 * @param clientId client ID of the service account from the console. May be null. 304 * @param clientEmail client email address of the service account from the console 305 * @param privateKeyPkcs8 RSA private key object for the service account in PKCS#8 format. 306 * @param privateKeyId private key identifier for the service account. May be null. 307 * @param scopes scope strings for the APIs to be called. May be null or an empty collection, 308 * which results in a credential that must have createScoped called before use. 309 * @param defaultScopes default scope strings for the APIs to be called. May be null or an empty 310 * collection, which results in a credential that must have createScoped called before use. 311 * @param transportFactory HTTP transport factory, creates the transport used to get access 312 * tokens. 313 * @param tokenServerUri URI of the end point that provides tokens 314 * @return new ServiceAccountCredentials created from a private key 315 * @throws IOException if the credential cannot be created from the private key 316 */ fromPkcs8( String clientId, String clientEmail, String privateKeyPkcs8, String privateKeyId, Collection<String> scopes, Collection<String> defaultScopes, HttpTransportFactory transportFactory, URI tokenServerUri)317 public static ServiceAccountCredentials fromPkcs8( 318 String clientId, 319 String clientEmail, 320 String privateKeyPkcs8, 321 String privateKeyId, 322 Collection<String> scopes, 323 Collection<String> defaultScopes, 324 HttpTransportFactory transportFactory, 325 URI tokenServerUri) 326 throws IOException { 327 328 ServiceAccountCredentials.Builder builder = 329 ServiceAccountCredentials.newBuilder() 330 .setClientId(clientId) 331 .setClientEmail(clientEmail) 332 .setPrivateKeyId(privateKeyId) 333 .setScopes(scopes, defaultScopes) 334 .setHttpTransportFactory(transportFactory) 335 .setTokenServerUri(tokenServerUri); 336 337 return fromPkcs8(privateKeyPkcs8, builder); 338 } 339 340 /** 341 * Factory with minimum identifying information and custom transport using PKCS#8 for the private 342 * key. 343 * 344 * @param clientId Client ID of the service account from the console. May be null. 345 * @param clientEmail Client email address of the service account from the console. 346 * @param privateKeyPkcs8 RSA private key object for the service account in PKCS#8 format. 347 * @param privateKeyId Private key identifier for the service account. May be null. 348 * @param scopes Scope strings for the APIs to be called. May be null or an empty collection, 349 * which results in a credential that must have createScoped called before use. 350 * @param transportFactory HTTP transport factory, creates the transport used to get access 351 * tokens. 352 * @param tokenServerUri URI of the end point that provides tokens. 353 * @param serviceAccountUser The email of the user account to impersonate, if delegating 354 * domain-wide authority to the service account. 355 * @return New ServiceAccountCredentials created from a private key. 356 * @throws IOException if the credential cannot be created from the private key. 357 */ fromPkcs8( String clientId, String clientEmail, String privateKeyPkcs8, String privateKeyId, Collection<String> scopes, HttpTransportFactory transportFactory, URI tokenServerUri, String serviceAccountUser)358 public static ServiceAccountCredentials fromPkcs8( 359 String clientId, 360 String clientEmail, 361 String privateKeyPkcs8, 362 String privateKeyId, 363 Collection<String> scopes, 364 HttpTransportFactory transportFactory, 365 URI tokenServerUri, 366 String serviceAccountUser) 367 throws IOException { 368 369 ServiceAccountCredentials.Builder builder = 370 ServiceAccountCredentials.newBuilder() 371 .setClientId(clientId) 372 .setClientEmail(clientEmail) 373 .setPrivateKeyId(privateKeyId) 374 .setScopes(scopes) 375 .setHttpTransportFactory(transportFactory) 376 .setTokenServerUri(tokenServerUri) 377 .setServiceAccountUser(serviceAccountUser); 378 379 return fromPkcs8(privateKeyPkcs8, builder); 380 } 381 382 /** 383 * Factory with minimum identifying information and custom transport using PKCS#8 for the private 384 * key. 385 * 386 * @param clientId client ID of the service account from the console. May be null. 387 * @param clientEmail client email address of the service account from the console 388 * @param privateKeyPkcs8 RSA private key object for the service account in PKCS#8 format. 389 * @param privateKeyId private key identifier for the service account. May be null. 390 * @param scopes scope strings for the APIs to be called. May be null or an empty collection, 391 * which results in a credential that must have createScoped called before use. 392 * @param defaultScopes default scope strings for the APIs to be called. May be null or an empty 393 * collection, which results in a credential that must have createScoped called before use. 394 * @param transportFactory HTTP transport factory, creates the transport used to get access 395 * tokens. 396 * @param tokenServerUri URI of the end point that provides tokens 397 * @param serviceAccountUser the email of the user account to impersonate, if delegating 398 * domain-wide authority to the service account. 399 * @return new ServiceAccountCredentials created from a private key 400 * @throws IOException if the credential cannot be created from the private key 401 */ fromPkcs8( String clientId, String clientEmail, String privateKeyPkcs8, String privateKeyId, Collection<String> scopes, Collection<String> defaultScopes, HttpTransportFactory transportFactory, URI tokenServerUri, String serviceAccountUser)402 public static ServiceAccountCredentials fromPkcs8( 403 String clientId, 404 String clientEmail, 405 String privateKeyPkcs8, 406 String privateKeyId, 407 Collection<String> scopes, 408 Collection<String> defaultScopes, 409 HttpTransportFactory transportFactory, 410 URI tokenServerUri, 411 String serviceAccountUser) 412 throws IOException { 413 ServiceAccountCredentials.Builder builder = 414 ServiceAccountCredentials.newBuilder() 415 .setClientId(clientId) 416 .setClientEmail(clientEmail) 417 .setPrivateKeyId(privateKeyId) 418 .setScopes(scopes, defaultScopes) 419 .setHttpTransportFactory(transportFactory) 420 .setTokenServerUri(tokenServerUri) 421 .setServiceAccountUser(serviceAccountUser); 422 423 return fromPkcs8(privateKeyPkcs8, builder); 424 } 425 426 /** 427 * Internal constructor 428 * 429 * @param privateKeyPkcs8 RSA private key object for the service account in PKCS#8 format. 430 * @param builder A builder for {@link ServiceAccountCredentials} See {@link 431 * ServiceAccountCredentials.Builder} 432 * @return an instance of {@link ServiceAccountCredentials} 433 */ fromPkcs8( String privateKeyPkcs8, ServiceAccountCredentials.Builder builder)434 static ServiceAccountCredentials fromPkcs8( 435 String privateKeyPkcs8, ServiceAccountCredentials.Builder builder) throws IOException { 436 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(privateKeyPkcs8); 437 builder.setPrivateKey(privateKey); 438 439 return new ServiceAccountCredentials(builder); 440 } 441 442 /** 443 * Returns credentials defined by a Service Account key file in JSON format from the Google 444 * Developers Console. 445 * 446 * @param credentialsStream the stream with the credential definition. 447 * @return the credential defined by the credentialsStream. 448 * @throws IOException if the credential cannot be created from the stream. 449 */ fromStream(InputStream credentialsStream)450 public static ServiceAccountCredentials fromStream(InputStream credentialsStream) 451 throws IOException { 452 return fromStream(credentialsStream, OAuth2Utils.HTTP_TRANSPORT_FACTORY); 453 } 454 455 /** 456 * Returns credentials defined by a Service Account key file in JSON format from the Google 457 * Developers Console. 458 * 459 * @param credentialsStream the stream with the credential definition. 460 * @param transportFactory HTTP transport factory, creates the transport used to get access 461 * tokens. 462 * @return the credential defined by the credentialsStream. 463 * @throws IOException if the credential cannot be created from the stream. 464 */ fromStream( InputStream credentialsStream, HttpTransportFactory transportFactory)465 public static ServiceAccountCredentials fromStream( 466 InputStream credentialsStream, HttpTransportFactory transportFactory) throws IOException { 467 ServiceAccountCredentials credential = 468 (ServiceAccountCredentials) 469 GoogleCredentials.fromStream(credentialsStream, transportFactory); 470 if (credential == null) { 471 throw new IOException( 472 String.format( 473 "Error reading credentials from stream, ServiceAccountCredentials type is not recognized.")); 474 } 475 return credential; 476 } 477 478 /** Returns whether the scopes are empty, meaning createScoped must be called before use. */ 479 @Override createScopedRequired()480 public boolean createScopedRequired() { 481 return scopes.isEmpty() && defaultScopes.isEmpty(); 482 } 483 484 /** Returns true if credential is configured domain wide delegation */ 485 @VisibleForTesting isConfiguredForDomainWideDelegation()486 boolean isConfiguredForDomainWideDelegation() { 487 return serviceAccountUser != null && serviceAccountUser.length() > 0; 488 } 489 490 /** 491 * Refreshes the OAuth2 access token by getting a new access token using a JSON Web Token (JWT). 492 */ 493 @Override refreshAccessToken()494 public AccessToken refreshAccessToken() throws IOException { 495 JsonFactory jsonFactory = OAuth2Utils.JSON_FACTORY; 496 long currentTime = clock.currentTimeMillis(); 497 String assertion = createAssertion(jsonFactory, currentTime); 498 499 GenericData tokenRequest = new GenericData(); 500 tokenRequest.set("grant_type", GRANT_TYPE); 501 tokenRequest.set("assertion", assertion); 502 UrlEncodedContent content = new UrlEncodedContent(tokenRequest); 503 504 HttpRequestFactory requestFactory = transportFactory.create().createRequestFactory(); 505 HttpRequest request = requestFactory.buildPostRequest(new GenericUrl(tokenServerUri), content); 506 507 if (this.defaultRetriesEnabled) { 508 request.setNumberOfRetries(DEFAULT_NUMBER_OF_RETRIES); 509 } else { 510 request.setNumberOfRetries(0); 511 } 512 request.setParser(new JsonObjectParser(jsonFactory)); 513 514 ExponentialBackOff backoff = 515 new ExponentialBackOff.Builder() 516 .setInitialIntervalMillis(INITIAL_RETRY_INTERVAL_MILLIS) 517 .setRandomizationFactor(RETRY_RANDOMIZATION_FACTOR) 518 .setMultiplier(RETRY_MULTIPLIER) 519 .build(); 520 521 request.setUnsuccessfulResponseHandler( 522 new HttpBackOffUnsuccessfulResponseHandler(backoff) 523 .setBackOffRequired( 524 response -> { 525 int code = response.getStatusCode(); 526 return OAuth2Utils.TOKEN_ENDPOINT_RETRYABLE_STATUS_CODES.contains(code); 527 })); 528 529 request.setIOExceptionHandler(new HttpBackOffIOExceptionHandler(backoff)); 530 HttpResponse response; 531 532 String errorTemplate = "Error getting access token for service account: %s, iss: %s"; 533 534 try { 535 response = request.execute(); 536 } catch (HttpResponseException re) { 537 String message = String.format(errorTemplate, re.getMessage(), getIssuer()); 538 throw GoogleAuthException.createWithTokenEndpointResponseException(re, message); 539 } catch (IOException e) { 540 throw GoogleAuthException.createWithTokenEndpointIOException( 541 e, String.format(errorTemplate, e.getMessage(), getIssuer())); 542 } 543 544 GenericData responseData = response.parseAs(GenericData.class); 545 String accessToken = 546 OAuth2Utils.validateString(responseData, "access_token", PARSE_ERROR_PREFIX); 547 int expiresInSeconds = 548 OAuth2Utils.validateInt32(responseData, "expires_in", PARSE_ERROR_PREFIX); 549 long expiresAtMilliseconds = clock.currentTimeMillis() + expiresInSeconds * 1000L; 550 return new AccessToken(accessToken, new Date(expiresAtMilliseconds)); 551 } 552 553 /** 554 * Returns a Google ID Token from the metadata server on ComputeEngine. 555 * 556 * @param targetAudience the aud: field the IdToken should include. 557 * @param options list of Credential specific options for the token. Currently, unused for 558 * ServiceAccountCredentials. 559 * @throws IOException if the attempt to get an IdToken failed 560 * @return IdToken object which includes the raw id_token, expiration and audience 561 */ 562 @Override idTokenWithAudience(String targetAudience, List<Option> options)563 public IdToken idTokenWithAudience(String targetAudience, List<Option> options) 564 throws IOException { 565 566 JsonFactory jsonFactory = OAuth2Utils.JSON_FACTORY; 567 long currentTime = clock.currentTimeMillis(); 568 String assertion = 569 createAssertionForIdToken( 570 jsonFactory, currentTime, tokenServerUri.toString(), targetAudience); 571 572 GenericData tokenRequest = new GenericData(); 573 tokenRequest.set("grant_type", GRANT_TYPE); 574 tokenRequest.set("assertion", assertion); 575 UrlEncodedContent content = new UrlEncodedContent(tokenRequest); 576 577 HttpRequestFactory requestFactory = transportFactory.create().createRequestFactory(); 578 HttpRequest request = requestFactory.buildPostRequest(new GenericUrl(tokenServerUri), content); 579 request.setParser(new JsonObjectParser(jsonFactory)); 580 HttpResponse response; 581 try { 582 response = request.execute(); 583 } catch (IOException e) { 584 throw new IOException( 585 String.format( 586 "Error getting id token for service account: %s, iss: %s", 587 e.getMessage(), getIssuer()), 588 e); 589 } 590 591 GenericData responseData = response.parseAs(GenericData.class); 592 String rawToken = OAuth2Utils.validateString(responseData, "id_token", PARSE_ERROR_PREFIX); 593 594 return IdToken.create(rawToken); 595 } 596 597 /** 598 * Clones the service account with the specified default retries. 599 * 600 * @param defaultRetriesEnabled a flag enabling or disabling default retries 601 * @return GoogleCredentials with the specified retry configuration. 602 */ 603 @Override createWithCustomRetryStrategy(boolean defaultRetriesEnabled)604 public ServiceAccountCredentials createWithCustomRetryStrategy(boolean defaultRetriesEnabled) { 605 return this.toBuilder().setDefaultRetriesEnabled(defaultRetriesEnabled).build(); 606 } 607 608 /** 609 * Clones the service account with the specified scopes. 610 * 611 * <p>Should be called before use for instances with empty scopes. 612 */ 613 @Override createScoped(Collection<String> newScopes)614 public GoogleCredentials createScoped(Collection<String> newScopes) { 615 return createScoped(newScopes, null); 616 } 617 618 /** 619 * Clones the service account with the specified scopes. 620 * 621 * <p>Should be called before use for instances with empty scopes. 622 */ 623 @Override createScoped( Collection<String> newScopes, Collection<String> newDefaultScopes)624 public GoogleCredentials createScoped( 625 Collection<String> newScopes, Collection<String> newDefaultScopes) { 626 return this.toBuilder().setScopes(newScopes, newDefaultScopes).build(); 627 } 628 629 /** 630 * Clones the service account with a new lifetime value. 631 * 632 * @param lifetime life time value in seconds. The value should be at most 43200 (12 hours). If 633 * the token is used for calling a Google API, then the value should be at most 3600 (1 hour). 634 * If the given value is 0, then the default value 3600 will be used when creating the 635 * credentials. 636 * @return the cloned service account credentials with the given custom life time 637 */ createWithCustomLifetime(int lifetime)638 public ServiceAccountCredentials createWithCustomLifetime(int lifetime) { 639 return this.toBuilder().setLifetime(lifetime).build(); 640 } 641 642 /** 643 * Clones the service account with a new useJwtAccessWithScope value. This flag will be ignored if 644 * universeDomain field is different from {@link Credentials.GOOGLE_DEFAULT_UNIVERSE}. 645 * 646 * @param useJwtAccessWithScope whether self-signed JWT with scopes should be used 647 * @return the cloned service account credentials with the given useJwtAccessWithScope 648 */ createWithUseJwtAccessWithScope(boolean useJwtAccessWithScope)649 public ServiceAccountCredentials createWithUseJwtAccessWithScope(boolean useJwtAccessWithScope) { 650 return this.toBuilder().setUseJwtAccessWithScope(useJwtAccessWithScope).build(); 651 } 652 653 @Override createDelegated(String user)654 public GoogleCredentials createDelegated(String user) { 655 return this.toBuilder().setServiceAccountUser(user).build(); 656 } 657 getClientId()658 public final String getClientId() { 659 return clientId; 660 } 661 getClientEmail()662 public final String getClientEmail() { 663 return clientEmail; 664 } 665 getPrivateKey()666 public final PrivateKey getPrivateKey() { 667 return privateKey; 668 } 669 getPrivateKeyId()670 public final String getPrivateKeyId() { 671 return privateKeyId; 672 } 673 getScopes()674 public final Collection<String> getScopes() { 675 return scopes; 676 } 677 getDefaultScopes()678 public final Collection<String> getDefaultScopes() { 679 return defaultScopes; 680 } 681 getServiceAccountUser()682 public final String getServiceAccountUser() { 683 return serviceAccountUser; 684 } 685 getProjectId()686 public final String getProjectId() { 687 return projectId; 688 } 689 getTokenServerUri()690 public final URI getTokenServerUri() { 691 return tokenServerUri; 692 } 693 getIssuer()694 private String getIssuer() { 695 return this.clientEmail; 696 } 697 698 @VisibleForTesting getLifetime()699 int getLifetime() { 700 return lifetime; 701 } 702 getUseJwtAccessWithScope()703 public boolean getUseJwtAccessWithScope() { 704 return useJwtAccessWithScope; 705 } 706 707 @VisibleForTesting getSelfSignedJwtCredentialsWithScope()708 JwtCredentials getSelfSignedJwtCredentialsWithScope() { 709 return selfSignedJwtCredentialsWithScope; 710 } 711 712 @Override getAccount()713 public String getAccount() { 714 return getClientEmail(); 715 } 716 717 @Override sign(byte[] toSign)718 public byte[] sign(byte[] toSign) { 719 try { 720 Signature signer = Signature.getInstance(OAuth2Utils.SIGNATURE_ALGORITHM); 721 signer.initSign(getPrivateKey()); 722 signer.update(toSign); 723 return signer.sign(); 724 } catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException ex) { 725 throw new SigningException("Failed to sign the provided bytes", ex); 726 } 727 } 728 729 /** 730 * Returns a new JwtCredentials instance with modified claims. 731 * 732 * @param newClaims new claims. Any unspecified claim fields will default to the current values. 733 * @return new credentials 734 */ 735 @Override jwtWithClaims(JwtClaims newClaims)736 public JwtCredentials jwtWithClaims(JwtClaims newClaims) { 737 JwtClaims.Builder claimsBuilder = 738 JwtClaims.newBuilder().setIssuer(getIssuer()).setSubject(clientEmail); 739 return JwtCredentials.newBuilder() 740 .setPrivateKey(privateKey) 741 .setPrivateKeyId(privateKeyId) 742 .setJwtClaims(claimsBuilder.build().merge(newClaims)) 743 .setClock(clock) 744 .build(); 745 } 746 747 @Override hashCode()748 public int hashCode() { 749 return Objects.hash( 750 clientId, 751 clientEmail, 752 privateKey, 753 privateKeyId, 754 transportFactoryClassName, 755 tokenServerUri, 756 scopes, 757 defaultScopes, 758 lifetime, 759 useJwtAccessWithScope, 760 defaultRetriesEnabled, 761 super.hashCode()); 762 } 763 764 @Override toStringHelper()765 protected ToStringHelper toStringHelper() { 766 return super.toStringHelper() 767 .add("clientId", clientId) 768 .add("clientEmail", clientEmail) 769 .add("privateKeyId", privateKeyId) 770 .add("transportFactoryClassName", transportFactoryClassName) 771 .add("tokenServerUri", tokenServerUri) 772 .add("scopes", scopes) 773 .add("defaultScopes", defaultScopes) 774 .add("serviceAccountUser", serviceAccountUser) 775 .add("lifetime", lifetime) 776 .add("useJwtAccessWithScope", useJwtAccessWithScope) 777 .add("defaultRetriesEnabled", defaultRetriesEnabled); 778 } 779 780 @Override equals(Object obj)781 public boolean equals(Object obj) { 782 if (!(obj instanceof ServiceAccountCredentials)) { 783 return false; 784 } 785 if (!super.equals(obj)) { 786 return false; 787 } 788 789 ServiceAccountCredentials other = (ServiceAccountCredentials) obj; 790 return Objects.equals(this.clientId, other.clientId) 791 && Objects.equals(this.clientEmail, other.clientEmail) 792 && Objects.equals(this.privateKey, other.privateKey) 793 && Objects.equals(this.privateKeyId, other.privateKeyId) 794 && Objects.equals(this.transportFactoryClassName, other.transportFactoryClassName) 795 && Objects.equals(this.tokenServerUri, other.tokenServerUri) 796 && Objects.equals(this.scopes, other.scopes) 797 && Objects.equals(this.defaultScopes, other.defaultScopes) 798 && Objects.equals(this.lifetime, other.lifetime) 799 && Objects.equals(this.useJwtAccessWithScope, other.useJwtAccessWithScope) 800 && Objects.equals(this.defaultRetriesEnabled, other.defaultRetriesEnabled); 801 } 802 createAssertion(JsonFactory jsonFactory, long currentTime)803 String createAssertion(JsonFactory jsonFactory, long currentTime) throws IOException { 804 JsonWebSignature.Header header = new JsonWebSignature.Header(); 805 header.setAlgorithm("RS256"); 806 header.setType("JWT"); 807 header.setKeyId(privateKeyId); 808 809 JsonWebToken.Payload payload = new JsonWebToken.Payload(); 810 payload.setIssuer(getIssuer()); 811 payload.setIssuedAtTimeSeconds(currentTime / 1000); 812 payload.setExpirationTimeSeconds(currentTime / 1000 + this.lifetime); 813 payload.setSubject(serviceAccountUser); 814 if (scopes.isEmpty()) { 815 payload.put("scope", Joiner.on(' ').join(defaultScopes)); 816 } else { 817 payload.put("scope", Joiner.on(' ').join(scopes)); 818 } 819 820 payload.setAudience(OAuth2Utils.TOKEN_SERVER_URI.toString()); 821 String assertion; 822 823 try { 824 assertion = JsonWebSignature.signUsingRsaSha256(privateKey, jsonFactory, header, payload); 825 } catch (GeneralSecurityException e) { 826 throw new IOException( 827 "Error signing service account access token request with private key.", e); 828 } 829 return assertion; 830 } 831 832 @VisibleForTesting createAssertionForIdToken( JsonFactory jsonFactory, long currentTime, String audience, String targetAudience)833 String createAssertionForIdToken( 834 JsonFactory jsonFactory, long currentTime, String audience, String targetAudience) 835 throws IOException { 836 JsonWebSignature.Header header = new JsonWebSignature.Header(); 837 header.setAlgorithm("RS256"); 838 header.setType("JWT"); 839 header.setKeyId(privateKeyId); 840 841 JsonWebToken.Payload payload = new JsonWebToken.Payload(); 842 payload.setIssuer(getIssuer()); 843 payload.setIssuedAtTimeSeconds(currentTime / 1000); 844 payload.setExpirationTimeSeconds(currentTime / 1000 + this.lifetime); 845 payload.setSubject(serviceAccountUser); 846 847 if (audience == null) { 848 payload.setAudience(OAuth2Utils.TOKEN_SERVER_URI.toString()); 849 } else { 850 payload.setAudience(audience); 851 } 852 853 try { 854 payload.set("target_audience", targetAudience); 855 856 String assertion = 857 JsonWebSignature.signUsingRsaSha256(privateKey, jsonFactory, header, payload); 858 return assertion; 859 } catch (GeneralSecurityException e) { 860 throw new IOException( 861 "Error signing service account access token request with private key.", e); 862 } 863 } 864 865 /** 866 * Self-signed JWT uses uri as audience, which should have the "https://{host}/" format. For 867 * instance, if the uri is "https://compute.googleapis.com/compute/v1/projects/", then this 868 * function returns "https://compute.googleapis.com/". 869 */ 870 @VisibleForTesting getUriForSelfSignedJWT(URI uri)871 static URI getUriForSelfSignedJWT(URI uri) { 872 if (uri == null || uri.getScheme() == null || uri.getHost() == null) { 873 return uri; 874 } 875 try { 876 return new URI(uri.getScheme(), uri.getHost(), "/", null); 877 } catch (URISyntaxException unused) { 878 return uri; 879 } 880 } 881 882 @VisibleForTesting createSelfSignedJwtCredentials(final URI uri)883 JwtCredentials createSelfSignedJwtCredentials(final URI uri) { 884 // Create a JwtCredentials for self-signed JWT. See https://google.aip.dev/auth/4111. 885 JwtClaims.Builder claimsBuilder = 886 JwtClaims.newBuilder().setIssuer(clientEmail).setSubject(clientEmail); 887 888 if (uri == null) { 889 // If uri is null, use scopes. 890 String scopeClaim = ""; 891 if (!scopes.isEmpty()) { 892 scopeClaim = Joiner.on(' ').join(scopes); 893 } else { 894 scopeClaim = Joiner.on(' ').join(defaultScopes); 895 } 896 claimsBuilder.setAdditionalClaims(Collections.singletonMap("scope", scopeClaim)); 897 } else { 898 // otherwise, use audience with the uri. 899 claimsBuilder.setAudience(getUriForSelfSignedJWT(uri).toString()); 900 } 901 return JwtCredentials.newBuilder() 902 .setPrivateKey(privateKey) 903 .setPrivateKeyId(privateKeyId) 904 .setJwtClaims(claimsBuilder.build()) 905 .setClock(clock) 906 .build(); 907 } 908 909 @Override getRequestMetadata( final URI uri, Executor executor, final RequestMetadataCallback callback)910 public void getRequestMetadata( 911 final URI uri, Executor executor, final RequestMetadataCallback callback) { 912 // For default universe Self-signed JWT could be explicitly disabled with 913 // {@code ServiceAccountCredentials.useJwtAccessWithScope} flag. 914 // If universe is non-default, it only supports self-signed JWT, and it is always allowed. 915 if (this.useJwtAccessWithScope || !isDefaultUniverseDomain()) { 916 // This will call getRequestMetadata(URI uri), which handles self-signed JWT logic. 917 // Self-signed JWT doesn't use network, so here we do a blocking call to improve 918 // efficiency. executor will be ignored since it is intended for async operation. 919 blockingGetToCallback(uri, callback); 920 } else { 921 super.getRequestMetadata(uri, executor, callback); 922 } 923 } 924 925 /** Provide the request metadata by putting an access JWT directly in the metadata. */ 926 @Override getRequestMetadata(URI uri)927 public Map<String, List<String>> getRequestMetadata(URI uri) throws IOException { 928 if (createScopedRequired() && uri == null) { 929 throw new IOException( 930 "Scopes and uri are not configured for service account. Specify the scopes" 931 + " by calling createScoped or passing scopes to constructor or" 932 + " providing uri to getRequestMetadata."); 933 } 934 935 if (isDefaultUniverseDomain()) { 936 return getRequestMetadataForGdu(uri); 937 } else { 938 return getRequestMetadataForNonGdu(uri); 939 } 940 } 941 getRequestMetadataForGdu(URI uri)942 private Map<String, List<String>> getRequestMetadataForGdu(URI uri) throws IOException { 943 // If scopes are provided, but we cannot use self-signed JWT or domain-wide delegation is 944 // configured then use scopes to get access token. 945 if ((!createScopedRequired() && !useJwtAccessWithScope) 946 || isConfiguredForDomainWideDelegation()) { 947 return super.getRequestMetadata(uri); 948 } 949 950 return getRequestMetadataWithSelfSignedJwt(uri); 951 } 952 getRequestMetadataForNonGdu(URI uri)953 private Map<String, List<String>> getRequestMetadataForNonGdu(URI uri) throws IOException { 954 // Self Signed JWT is not supported for domain-wide delegation for non-GDU universes 955 if (isConfiguredForDomainWideDelegation()) { 956 throw new IOException( 957 String.format( 958 "Service Account user is configured for the credential. " 959 + "Domain-wide delegation is not supported in universes different than %s.", 960 Credentials.GOOGLE_DEFAULT_UNIVERSE)); 961 } 962 963 return getRequestMetadataWithSelfSignedJwt(uri); 964 } 965 966 /** Provide the access JWT for scopes if provided, for uri as aud otherwise */ 967 @VisibleForTesting getRequestMetadataWithSelfSignedJwt(URI uri)968 private Map<String, List<String>> getRequestMetadataWithSelfSignedJwt(URI uri) 969 throws IOException { 970 // If scopes are provided and self-signed JWT can be used, use self-signed JWT with scopes. 971 // Otherwise, use self-signed JWT with uri as the audience. 972 JwtCredentials jwtCredentials; 973 if (!createScopedRequired()) { 974 // Create selfSignedJwtCredentialsWithScope when needed and reuse it for better performance. 975 if (selfSignedJwtCredentialsWithScope == null) { 976 selfSignedJwtCredentialsWithScope = createSelfSignedJwtCredentials(null); 977 } 978 jwtCredentials = selfSignedJwtCredentialsWithScope; 979 } else { 980 // Create JWT credentials with the uri as audience. 981 jwtCredentials = createSelfSignedJwtCredentials(uri); 982 } 983 984 Map<String, List<String>> requestMetadata = jwtCredentials.getRequestMetadata(null); 985 return addQuotaProjectIdToRequestMetadata(quotaProjectId, requestMetadata); 986 } 987 988 @SuppressWarnings("unused") readObject(ObjectInputStream input)989 private void readObject(ObjectInputStream input) throws IOException, ClassNotFoundException { 990 // properly deserialize the transient transportFactory 991 input.defaultReadObject(); 992 transportFactory = newInstance(transportFactoryClassName); 993 } 994 newBuilder()995 public static Builder newBuilder() { 996 return new Builder(); 997 } 998 999 @Override toBuilder()1000 public Builder toBuilder() { 1001 return new Builder(this); 1002 } 1003 1004 public static class Builder extends GoogleCredentials.Builder { 1005 1006 private String clientId; 1007 private String clientEmail; 1008 private PrivateKey privateKey; 1009 private String privateKeyId; 1010 private String serviceAccountUser; 1011 private String projectId; 1012 private URI tokenServerUri; 1013 private Collection<String> scopes; 1014 private Collection<String> defaultScopes; 1015 private HttpTransportFactory transportFactory; 1016 private int lifetime = DEFAULT_LIFETIME_IN_SECONDS; 1017 private boolean useJwtAccessWithScope = false; 1018 private boolean defaultRetriesEnabled = true; 1019 Builder()1020 protected Builder() {} 1021 Builder(ServiceAccountCredentials credentials)1022 protected Builder(ServiceAccountCredentials credentials) { 1023 super(credentials); 1024 this.clientId = credentials.clientId; 1025 this.clientEmail = credentials.clientEmail; 1026 this.privateKey = credentials.privateKey; 1027 this.privateKeyId = credentials.privateKeyId; 1028 this.scopes = credentials.scopes; 1029 this.defaultScopes = credentials.defaultScopes; 1030 this.transportFactory = credentials.transportFactory; 1031 this.tokenServerUri = credentials.tokenServerUri; 1032 this.serviceAccountUser = credentials.serviceAccountUser; 1033 this.projectId = credentials.projectId; 1034 this.lifetime = credentials.lifetime; 1035 this.useJwtAccessWithScope = credentials.useJwtAccessWithScope; 1036 this.defaultRetriesEnabled = credentials.defaultRetriesEnabled; 1037 } 1038 1039 @CanIgnoreReturnValue setClientId(String clientId)1040 public Builder setClientId(String clientId) { 1041 this.clientId = clientId; 1042 return this; 1043 } 1044 1045 @CanIgnoreReturnValue setClientEmail(String clientEmail)1046 public Builder setClientEmail(String clientEmail) { 1047 this.clientEmail = clientEmail; 1048 return this; 1049 } 1050 1051 @CanIgnoreReturnValue setPrivateKey(PrivateKey privateKey)1052 public Builder setPrivateKey(PrivateKey privateKey) { 1053 this.privateKey = privateKey; 1054 return this; 1055 } 1056 1057 @CanIgnoreReturnValue setPrivateKeyString(String privateKeyPkcs8)1058 public Builder setPrivateKeyString(String privateKeyPkcs8) throws IOException { 1059 this.privateKey = OAuth2Utils.privateKeyFromPkcs8(privateKeyPkcs8); 1060 return this; 1061 } 1062 1063 @CanIgnoreReturnValue setPrivateKeyId(String privateKeyId)1064 public Builder setPrivateKeyId(String privateKeyId) { 1065 this.privateKeyId = privateKeyId; 1066 return this; 1067 } 1068 1069 @CanIgnoreReturnValue setScopes(Collection<String> scopes)1070 public Builder setScopes(Collection<String> scopes) { 1071 this.scopes = scopes; 1072 this.defaultScopes = ImmutableSet.<String>of(); 1073 return this; 1074 } 1075 1076 @CanIgnoreReturnValue setScopes(Collection<String> scopes, Collection<String> defaultScopes)1077 public Builder setScopes(Collection<String> scopes, Collection<String> defaultScopes) { 1078 this.scopes = scopes; 1079 this.defaultScopes = defaultScopes; 1080 return this; 1081 } 1082 1083 @CanIgnoreReturnValue setServiceAccountUser(String serviceAccountUser)1084 public Builder setServiceAccountUser(String serviceAccountUser) { 1085 this.serviceAccountUser = serviceAccountUser; 1086 return this; 1087 } 1088 1089 @CanIgnoreReturnValue setProjectId(String projectId)1090 public Builder setProjectId(String projectId) { 1091 this.projectId = projectId; 1092 return this; 1093 } 1094 1095 @CanIgnoreReturnValue setTokenServerUri(URI tokenServerUri)1096 public Builder setTokenServerUri(URI tokenServerUri) { 1097 this.tokenServerUri = tokenServerUri; 1098 return this; 1099 } 1100 1101 @CanIgnoreReturnValue setHttpTransportFactory(HttpTransportFactory transportFactory)1102 public Builder setHttpTransportFactory(HttpTransportFactory transportFactory) { 1103 this.transportFactory = transportFactory; 1104 return this; 1105 } 1106 1107 @Override 1108 @CanIgnoreReturnValue setQuotaProjectId(String quotaProjectId)1109 public Builder setQuotaProjectId(String quotaProjectId) { 1110 super.setQuotaProjectId(quotaProjectId); 1111 return this; 1112 } 1113 1114 @CanIgnoreReturnValue setLifetime(int lifetime)1115 public Builder setLifetime(int lifetime) { 1116 this.lifetime = lifetime == 0 ? DEFAULT_LIFETIME_IN_SECONDS : lifetime; 1117 return this; 1118 } 1119 1120 /** 1121 * Sets the useJwtAccessWithScope flag. This flag will be ignored if universeDomain field is 1122 * different from {@link Credentials.GOOGLE_DEFAULT_UNIVERSE}. 1123 */ 1124 @CanIgnoreReturnValue setUseJwtAccessWithScope(boolean useJwtAccessWithScope)1125 public Builder setUseJwtAccessWithScope(boolean useJwtAccessWithScope) { 1126 this.useJwtAccessWithScope = useJwtAccessWithScope; 1127 return this; 1128 } 1129 1130 @CanIgnoreReturnValue setDefaultRetriesEnabled(boolean defaultRetriesEnabled)1131 public Builder setDefaultRetriesEnabled(boolean defaultRetriesEnabled) { 1132 this.defaultRetriesEnabled = defaultRetriesEnabled; 1133 return this; 1134 } 1135 setUniverseDomain(String universeDomain)1136 public Builder setUniverseDomain(String universeDomain) { 1137 super.universeDomain = universeDomain; 1138 return this; 1139 } 1140 getClientId()1141 public String getClientId() { 1142 return clientId; 1143 } 1144 getClientEmail()1145 public String getClientEmail() { 1146 return clientEmail; 1147 } 1148 getPrivateKey()1149 public PrivateKey getPrivateKey() { 1150 return privateKey; 1151 } 1152 getPrivateKeyId()1153 public String getPrivateKeyId() { 1154 return privateKeyId; 1155 } 1156 getScopes()1157 public Collection<String> getScopes() { 1158 return scopes; 1159 } 1160 getDefaultScopes()1161 public Collection<String> getDefaultScopes() { 1162 return defaultScopes; 1163 } 1164 getServiceAccountUser()1165 public String getServiceAccountUser() { 1166 return serviceAccountUser; 1167 } 1168 getProjectId()1169 public String getProjectId() { 1170 return projectId; 1171 } 1172 getTokenServerUri()1173 public URI getTokenServerUri() { 1174 return tokenServerUri; 1175 } 1176 getHttpTransportFactory()1177 public HttpTransportFactory getHttpTransportFactory() { 1178 return transportFactory; 1179 } 1180 getLifetime()1181 public int getLifetime() { 1182 return lifetime; 1183 } 1184 getUseJwtAccessWithScope()1185 public boolean getUseJwtAccessWithScope() { 1186 return useJwtAccessWithScope; 1187 } 1188 isDefaultRetriesEnabled()1189 public boolean isDefaultRetriesEnabled() { 1190 return defaultRetriesEnabled; 1191 } 1192 1193 @Override build()1194 public ServiceAccountCredentials build() { 1195 return new ServiceAccountCredentials(this); 1196 } 1197 } 1198 } 1199