@@ -103,9 +103,6 @@ public class ImpersonatedCredentials extends GoogleCredentials
103103 private static final int DEFAULT_LIFETIME_IN_SECONDS = 3600 ;
104104 private static final String CLOUD_PLATFORM_SCOPE =
105105 "https://www.googleapis.com/auth/cloud-platform" ;
106- private static final String IAM_ACCESS_TOKEN_ENDPOINT =
107- "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/%s:generateAccessToken" ;
108-
109106 private GoogleCredentials sourceCredentials ;
110107 private String targetPrincipal ;
111108 private List <String > delegates ;
@@ -423,14 +420,7 @@ public boolean createScopedRequired() {
423420
424421 @ Override
425422 public GoogleCredentials createScoped (Collection <String > scopes ) {
426- return toBuilder ()
427- .setScopes (new ArrayList <>(scopes ))
428- .setLifetime (this .lifetime )
429- .setDelegates (this .delegates )
430- .setHttpTransportFactory (this .transportFactory )
431- .setQuotaProjectId (this .quotaProjectId )
432- .setIamEndpointOverride (this .iamEndpointOverride )
433- .build ();
423+ return toBuilder ().setScopes (new ArrayList <>(scopes )).setAccessToken (null ).build ();
434424 }
435425
436426 @ Override
@@ -457,7 +447,7 @@ public ImpersonatedCredentials createWithCustomCalendar(Calendar calendar) {
457447 .build ();
458448 }
459449
460- private ImpersonatedCredentials (Builder builder ) {
450+ private ImpersonatedCredentials (Builder builder ) throws IOException {
461451 super (builder );
462452 this .sourceCredentials = builder .getSourceCredentials ();
463453 this .targetPrincipal = builder .getTargetPrincipal ();
@@ -472,14 +462,36 @@ private ImpersonatedCredentials(Builder builder) {
472462 this .transportFactoryClassName = this .transportFactory .getClass ().getName ();
473463 this .calendar = builder .getCalendar ();
474464 if (this .delegates == null ) {
475- this .delegates = new ArrayList <String >();
465+ this .delegates = new ArrayList <>();
476466 }
477467 if (this .scopes == null ) {
478468 throw new IllegalStateException ("Scopes cannot be null" );
479469 }
480470 if (this .lifetime > TWELVE_HOURS_IN_SECONDS ) {
481471 throw new IllegalStateException ("lifetime must be less than or equal to 43200" );
482472 }
473+
474+ // Do not expect explicit universe domain, throw exception if the explicit universe domain
475+ // does not match the source credential.
476+ // Do nothing if it matches the source credential
477+ if (isExplicitUniverseDomain ()
478+ && !this .sourceCredentials .getUniverseDomain ().equals (builder .getUniverseDomain ())) {
479+ throw new IllegalStateException (
480+ String .format (
481+ "Universe domain %s in source credentials "
482+ + "does not match %s universe domain set for impersonated credentials." ,
483+ this .sourceCredentials .getUniverseDomain (), builder .getUniverseDomain ()));
484+ }
485+ }
486+
487+ /**
488+ * Gets the universe domain for the credential.
489+ *
490+ * @return the universe domain from source credentials
491+ */
492+ @ Override
493+ public String getUniverseDomain () throws IOException {
494+ return this .sourceCredentials .getUniverseDomain ();
483495 }
484496
485497 @ Override
@@ -489,10 +501,18 @@ public AccessToken refreshAccessToken() throws IOException {
489501 this .sourceCredentials .createScoped (Arrays .asList (CLOUD_PLATFORM_SCOPE ));
490502 }
491503
492- try {
493- this .sourceCredentials .refreshIfExpired ();
494- } catch (IOException e ) {
495- throw new IOException ("Unable to refresh sourceCredentials" , e );
504+ // skip for SA with SSJ flow because it uses self-signed JWT
505+ // and will get refreshed at initialize request step
506+ // run for other source credential types or SA with GDU assert flow
507+ if (!(this .sourceCredentials instanceof ServiceAccountCredentials )
508+ || (isDefaultUniverseDomain ()
509+ && ((ServiceAccountCredentials ) this .sourceCredentials )
510+ .shouldUseAssertionFlowForGdu ())) {
511+ try {
512+ this .sourceCredentials .refreshIfExpired ();
513+ } catch (IOException e ) {
514+ throw new IOException ("Unable to refresh sourceCredentials" , e );
515+ }
496516 }
497517
498518 HttpTransport httpTransport = this .transportFactory .create ();
@@ -504,7 +524,11 @@ public AccessToken refreshAccessToken() throws IOException {
504524 String endpointUrl =
505525 this .iamEndpointOverride != null
506526 ? this .iamEndpointOverride
507- : String .format (IAM_ACCESS_TOKEN_ENDPOINT , this .targetPrincipal );
527+ : String .format (
528+ OAuth2Utils .IAM_ACCESS_TOKEN_ENDPOINT_FORMAT ,
529+ getUniverseDomain (),
530+ this .targetPrincipal );
531+
508532 GenericUrl url = new GenericUrl (endpointUrl );
509533
510534 Map <String , Object > body =
@@ -603,6 +627,9 @@ public boolean equals(Object obj) {
603627 if (!(obj instanceof ImpersonatedCredentials )) {
604628 return false ;
605629 }
630+ if (!super .equals (obj )) {
631+ return false ;
632+ }
606633 ImpersonatedCredentials other = (ImpersonatedCredentials ) obj ;
607634 return Objects .equals (this .sourceCredentials , other .sourceCredentials )
608635 && Objects .equals (this .targetPrincipal , other .targetPrincipal )
@@ -616,7 +643,7 @@ public boolean equals(Object obj) {
616643
617644 @ Override
618645 public Builder toBuilder () {
619- return new Builder (this . sourceCredentials , this . targetPrincipal );
646+ return new Builder (this );
620647 }
621648
622649 public static Builder newBuilder () {
@@ -636,11 +663,29 @@ public static class Builder extends GoogleCredentials.Builder {
636663
637664 protected Builder () {}
638665
666+ /**
667+ * @param sourceCredentials The source credentials to use for impersonation.
668+ * @param targetPrincipal The service account to impersonate.
669+ * @deprecated Use {@link #Builder(ImpersonatedCredentials)} instead. This constructor will be
670+ * removed in a future release.
671+ */
672+ @ Deprecated
639673 protected Builder (GoogleCredentials sourceCredentials , String targetPrincipal ) {
640674 this .sourceCredentials = sourceCredentials ;
641675 this .targetPrincipal = targetPrincipal ;
642676 }
643677
678+ protected Builder (ImpersonatedCredentials credentials ) {
679+ super (credentials );
680+ this .sourceCredentials = credentials .sourceCredentials ;
681+ this .targetPrincipal = credentials .targetPrincipal ;
682+ this .delegates = credentials .delegates ;
683+ this .scopes = credentials .scopes ;
684+ this .lifetime = credentials .lifetime ;
685+ this .transportFactory = credentials .transportFactory ;
686+ this .iamEndpointOverride = credentials .iamEndpointOverride ;
687+ }
688+
644689 @ CanIgnoreReturnValue
645690 public Builder setSourceCredentials (GoogleCredentials sourceCredentials ) {
646691 this .sourceCredentials = sourceCredentials ;
@@ -726,7 +771,13 @@ public Calendar getCalendar() {
726771
727772 @ Override
728773 public ImpersonatedCredentials build () {
729- return new ImpersonatedCredentials (this );
774+ try {
775+ return new ImpersonatedCredentials (this );
776+ } catch (IOException e ) {
777+ // throwing exception would be breaking change. catching instead.
778+ // this should never happen.
779+ throw new IllegalStateException (e );
780+ }
730781 }
731782 }
732783
0 commit comments