7676from google .auth import _helpers
7777from google .auth import _service_account_info
7878from google .auth import credentials
79+ from google .auth import exceptions
7980from google .auth import jwt
8081from google .oauth2 import _client
8182
8283_DEFAULT_TOKEN_LIFETIME_SECS = 3600 # 1 hour in seconds
84+ _DEFAULT_UNIVERSE_DOMAIN = "googleapis.com"
8385_GOOGLE_OAUTH2_TOKEN_ENDPOINT = "https://oauth2.googleapis.com/token"
8486
8587
@@ -136,6 +138,7 @@ def __init__(
136138 quota_project_id = None ,
137139 additional_claims = None ,
138140 always_use_jwt_access = False ,
141+ universe_domain = _DEFAULT_UNIVERSE_DOMAIN ,
139142 ):
140143 """
141144 Args:
@@ -156,6 +159,9 @@ def __init__(
156159 the JWT assertion used in the authorization grant.
157160 always_use_jwt_access (Optional[bool]): Whether self signed JWT should
158161 be always used.
162+ universe_domain (str): The universe domain. The default
163+ universe domain is googleapis.com. For default value self
164+ signed jwt is used for token refresh.
159165
160166 .. note:: Typically one of the helper constructors
161167 :meth:`from_service_account_file` or
@@ -173,6 +179,13 @@ def __init__(
173179 self ._quota_project_id = quota_project_id
174180 self ._token_uri = token_uri
175181 self ._always_use_jwt_access = always_use_jwt_access
182+ if not universe_domain :
183+ self ._universe_domain = _DEFAULT_UNIVERSE_DOMAIN
184+ else :
185+ self ._universe_domain = universe_domain
186+
187+ if universe_domain != _DEFAULT_UNIVERSE_DOMAIN :
188+ self ._always_use_jwt_access = True
176189
177190 self ._jwt_credentials = None
178191
@@ -202,6 +215,7 @@ def _from_signer_and_info(cls, signer, info, **kwargs):
202215 service_account_email = info ["client_email" ],
203216 token_uri = info ["token_uri" ],
204217 project_id = info .get ("project_id" ),
218+ universe_domain = info .get ("universe_domain" , _DEFAULT_UNIVERSE_DOMAIN ),
205219 ** kwargs
206220 )
207221
@@ -262,20 +276,28 @@ def requires_scopes(self):
262276 """
263277 return True if not self ._scopes else False
264278
265- @_helpers .copy_docstring (credentials .Scoped )
266- def with_scopes (self , scopes , default_scopes = None ):
267- return self .__class__ (
279+ def _make_copy (self ):
280+ cred = self .__class__ (
268281 self ._signer ,
269282 service_account_email = self ._service_account_email ,
270- scopes = scopes ,
271- default_scopes = default_scopes ,
283+ scopes = copy . copy ( self . _scopes ) ,
284+ default_scopes = copy . copy ( self . _default_scopes ) ,
272285 token_uri = self ._token_uri ,
273286 subject = self ._subject ,
274287 project_id = self ._project_id ,
275288 quota_project_id = self ._quota_project_id ,
276289 additional_claims = self ._additional_claims .copy (),
277290 always_use_jwt_access = self ._always_use_jwt_access ,
291+ universe_domain = self ._universe_domain ,
278292 )
293+ return cred
294+
295+ @_helpers .copy_docstring (credentials .Scoped )
296+ def with_scopes (self , scopes , default_scopes = None ):
297+ cred = self ._make_copy ()
298+ cred ._scopes = scopes
299+ cred ._default_scopes = default_scopes
300+ return cred
279301
280302 def with_always_use_jwt_access (self , always_use_jwt_access ):
281303 """Create a copy of these credentials with the specified always_use_jwt_access value.
@@ -286,19 +308,20 @@ def with_always_use_jwt_access(self, always_use_jwt_access):
286308 Returns:
287309 google.auth.service_account.Credentials: A new credentials
288310 instance.
311+ Raises:
312+ google.auth.exceptions.InvalidValue: If the universe domain is not
313+ default and always_use_jwt_access is False.
289314 """
290- return self .__class__ (
291- self ._signer ,
292- service_account_email = self ._service_account_email ,
293- scopes = self ._scopes ,
294- default_scopes = self ._default_scopes ,
295- token_uri = self ._token_uri ,
296- subject = self ._subject ,
297- project_id = self ._project_id ,
298- quota_project_id = self ._quota_project_id ,
299- additional_claims = self ._additional_claims .copy (),
300- always_use_jwt_access = always_use_jwt_access ,
301- )
315+ cred = self ._make_copy ()
316+ if (
317+ cred ._universe_domain != _DEFAULT_UNIVERSE_DOMAIN
318+ and not always_use_jwt_access
319+ ):
320+ raise exceptions .InvalidValue (
321+ "always_use_jwt_access should be True for non-default universe domain"
322+ )
323+ cred ._always_use_jwt_access = always_use_jwt_access
324+ return cred
302325
303326 def with_subject (self , subject ):
304327 """Create a copy of these credentials with the specified subject.
@@ -310,18 +333,9 @@ def with_subject(self, subject):
310333 google.auth.service_account.Credentials: A new credentials
311334 instance.
312335 """
313- return self .__class__ (
314- self ._signer ,
315- service_account_email = self ._service_account_email ,
316- scopes = self ._scopes ,
317- default_scopes = self ._default_scopes ,
318- token_uri = self ._token_uri ,
319- subject = subject ,
320- project_id = self ._project_id ,
321- quota_project_id = self ._quota_project_id ,
322- additional_claims = self ._additional_claims .copy (),
323- always_use_jwt_access = self ._always_use_jwt_access ,
324- )
336+ cred = self ._make_copy ()
337+ cred ._subject = subject
338+ return cred
325339
326340 def with_claims (self , additional_claims ):
327341 """Returns a copy of these credentials with modified claims.
@@ -337,51 +351,21 @@ def with_claims(self, additional_claims):
337351 """
338352 new_additional_claims = copy .deepcopy (self ._additional_claims )
339353 new_additional_claims .update (additional_claims or {})
340-
341- return self .__class__ (
342- self ._signer ,
343- service_account_email = self ._service_account_email ,
344- scopes = self ._scopes ,
345- default_scopes = self ._default_scopes ,
346- token_uri = self ._token_uri ,
347- subject = self ._subject ,
348- project_id = self ._project_id ,
349- quota_project_id = self ._quota_project_id ,
350- additional_claims = new_additional_claims ,
351- always_use_jwt_access = self ._always_use_jwt_access ,
352- )
354+ cred = self ._make_copy ()
355+ cred ._additional_claims = new_additional_claims
356+ return cred
353357
354358 @_helpers .copy_docstring (credentials .CredentialsWithQuotaProject )
355359 def with_quota_project (self , quota_project_id ):
356-
357- return self .__class__ (
358- self ._signer ,
359- service_account_email = self ._service_account_email ,
360- default_scopes = self ._default_scopes ,
361- scopes = self ._scopes ,
362- token_uri = self ._token_uri ,
363- subject = self ._subject ,
364- project_id = self ._project_id ,
365- quota_project_id = quota_project_id ,
366- additional_claims = self ._additional_claims .copy (),
367- always_use_jwt_access = self ._always_use_jwt_access ,
368- )
360+ cred = self ._make_copy ()
361+ cred ._quota_project_id = quota_project_id
362+ return cred
369363
370364 @_helpers .copy_docstring (credentials .CredentialsWithTokenUri )
371365 def with_token_uri (self , token_uri ):
372-
373- return self .__class__ (
374- self ._signer ,
375- service_account_email = self ._service_account_email ,
376- default_scopes = self ._default_scopes ,
377- scopes = self ._scopes ,
378- token_uri = token_uri ,
379- subject = self ._subject ,
380- project_id = self ._project_id ,
381- quota_project_id = self ._quota_project_id ,
382- additional_claims = self ._additional_claims .copy (),
383- always_use_jwt_access = self ._always_use_jwt_access ,
384- )
366+ cred = self ._make_copy ()
367+ cred ._token_uri = token_uri
368+ return cred
385369
386370 def _make_authorization_grant_assertion (self ):
387371 """Create the OAuth 2.0 assertion.
@@ -418,6 +402,18 @@ def _make_authorization_grant_assertion(self):
418402
419403 @_helpers .copy_docstring (credentials .Credentials )
420404 def refresh (self , request ):
405+ if (
406+ self ._universe_domain != _DEFAULT_UNIVERSE_DOMAIN
407+ and not self ._jwt_credentials
408+ ):
409+ raise exceptions .RefreshError (
410+ "self._jwt_credentials is missing for non-default universe domain"
411+ )
412+ if self ._universe_domain != _DEFAULT_UNIVERSE_DOMAIN and self ._subject :
413+ raise exceptions .RefreshError (
414+ "domain wide delegation is not supported for non-default universe domain"
415+ )
416+
421417 # Since domain wide delegation doesn't work with self signed JWT. If
422418 # subject exists, then we should not use self signed JWT.
423419 if self ._subject is None and self ._jwt_credentials is not None :
@@ -544,6 +540,7 @@ def __init__(
544540 target_audience ,
545541 additional_claims = None ,
546542 quota_project_id = None ,
543+ universe_domain = _DEFAULT_UNIVERSE_DOMAIN ,
547544 ):
548545 """
549546 Args:
@@ -556,6 +553,11 @@ def __init__(
556553 additional_claims (Mapping[str, str]): Any additional claims for
557554 the JWT assertion used in the authorization grant.
558555 quota_project_id (Optional[str]): The project ID used for quota and billing.
556+ universe_domain (str): The universe domain. The default
557+ universe domain is googleapis.com. For default value IAM ID
558+ token endponint is used for token refresh. Note that
559+ iam.serviceAccountTokenCreator role is required to use the IAM
560+ endpoint.
559561 .. note:: Typically one of the helper constructors
560562 :meth:`from_service_account_file` or
561563 :meth:`from_service_account_info` are used instead of calling the
@@ -569,6 +571,14 @@ def __init__(
569571 self ._quota_project_id = quota_project_id
570572 self ._use_iam_endpoint = False
571573
574+ if not universe_domain :
575+ self ._universe_domain = _DEFAULT_UNIVERSE_DOMAIN
576+ else :
577+ self ._universe_domain = universe_domain
578+
579+ if universe_domain != _DEFAULT_UNIVERSE_DOMAIN :
580+ self ._use_iam_endpoint = True
581+
572582 if additional_claims is not None :
573583 self ._additional_claims = additional_claims
574584 else :
@@ -592,6 +602,8 @@ def _from_signer_and_info(cls, signer, info, **kwargs):
592602 """
593603 kwargs .setdefault ("service_account_email" , info ["client_email" ])
594604 kwargs .setdefault ("token_uri" , info ["token_uri" ])
605+ if "universe_domain" in info :
606+ kwargs ["universe_domain" ] = info ["universe_domain" ]
595607 return cls (signer , ** kwargs )
596608
597609 @classmethod
@@ -632,6 +644,20 @@ def from_service_account_file(cls, filename, **kwargs):
632644 )
633645 return cls ._from_signer_and_info (signer , info , ** kwargs )
634646
647+ def _make_copy (self ):
648+ cred = self .__class__ (
649+ self ._signer ,
650+ service_account_email = self ._service_account_email ,
651+ token_uri = self ._token_uri ,
652+ target_audience = self ._target_audience ,
653+ additional_claims = self ._additional_claims .copy (),
654+ quota_project_id = self .quota_project_id ,
655+ universe_domain = self ._universe_domain ,
656+ )
657+ # _use_iam_endpoint is not exposed in the constructor
658+ cred ._use_iam_endpoint = self ._use_iam_endpoint
659+ return cred
660+
635661 def with_target_audience (self , target_audience ):
636662 """Create a copy of these credentials with the specified target
637663 audience.
@@ -644,14 +670,9 @@ def with_target_audience(self, target_audience):
644670 google.auth.service_account.IDTokenCredentials: A new credentials
645671 instance.
646672 """
647- return self .__class__ (
648- self ._signer ,
649- service_account_email = self ._service_account_email ,
650- token_uri = self ._token_uri ,
651- target_audience = target_audience ,
652- additional_claims = self ._additional_claims .copy (),
653- quota_project_id = self .quota_project_id ,
654- )
673+ cred = self ._make_copy ()
674+ cred ._target_audience = target_audience
675+ return cred
655676
656677 def _with_use_iam_endpoint (self , use_iam_endpoint ):
657678 """Create a copy of these credentials with the use_iam_endpoint value.
@@ -666,39 +687,29 @@ def _with_use_iam_endpoint(self, use_iam_endpoint):
666687 Returns:
667688 google.auth.service_account.IDTokenCredentials: A new credentials
668689 instance.
690+ Raises:
691+ google.auth.exceptions.InvalidValue: If the universe domain is not
692+ default and use_iam_endpoint is False.
669693 """
670- cred = self .__class__ (
671- self ._signer ,
672- service_account_email = self ._service_account_email ,
673- token_uri = self ._token_uri ,
674- target_audience = self ._target_audience ,
675- additional_claims = self ._additional_claims .copy (),
676- quota_project_id = self .quota_project_id ,
677- )
694+ cred = self ._make_copy ()
695+ if cred ._universe_domain != _DEFAULT_UNIVERSE_DOMAIN and not use_iam_endpoint :
696+ raise exceptions .InvalidValue (
697+ "use_iam_endpoint should be True for non-default universe domain"
698+ )
678699 cred ._use_iam_endpoint = use_iam_endpoint
679700 return cred
680701
681702 @_helpers .copy_docstring (credentials .CredentialsWithQuotaProject )
682703 def with_quota_project (self , quota_project_id ):
683- return self .__class__ (
684- self ._signer ,
685- service_account_email = self ._service_account_email ,
686- token_uri = self ._token_uri ,
687- target_audience = self ._target_audience ,
688- additional_claims = self ._additional_claims .copy (),
689- quota_project_id = quota_project_id ,
690- )
704+ cred = self ._make_copy ()
705+ cred ._quota_project_id = quota_project_id
706+ return cred
691707
692708 @_helpers .copy_docstring (credentials .CredentialsWithTokenUri )
693709 def with_token_uri (self , token_uri ):
694- return self .__class__ (
695- self ._signer ,
696- service_account_email = self ._service_account_email ,
697- token_uri = token_uri ,
698- target_audience = self ._target_audience ,
699- additional_claims = self ._additional_claims .copy (),
700- quota_project_id = self ._quota_project_id ,
701- )
710+ cred = self ._make_copy ()
711+ cred ._token_uri = token_uri
712+ return cred
702713
703714 def _make_authorization_grant_assertion (self ):
704715 """Create the OAuth 2.0 assertion.
0 commit comments