3636import datetime
3737import io
3838import json
39+ import re
3940
41+ from google .auth import _constants
4042from google .auth import _helpers
4143from google .auth import credentials
4244from google .auth import exceptions
@@ -50,6 +52,7 @@ class Credentials(
5052 credentials .CredentialsWithQuotaProject ,
5153 credentials .ReadOnlyScoped ,
5254 credentials .CredentialsWithTokenUri ,
55+ credentials .CredentialsWithTrustBoundary ,
5356):
5457 """Credentials for External Account Authorized Users.
5558
@@ -83,6 +86,7 @@ def __init__(
8386 scopes = None ,
8487 quota_project_id = None ,
8588 universe_domain = credentials .DEFAULT_UNIVERSE_DOMAIN ,
89+ trust_boundary = None ,
8690 ):
8791 """Instantiates a external account authorized user credentials object.
8892
@@ -108,6 +112,7 @@ def __init__(
108112 create the credentials.
109113 universe_domain (Optional[str]): The universe domain. The default value
110114 is googleapis.com.
115+ trust_boundary (Mapping[str,str]): A credential trust boundary.
111116
112117 Returns:
113118 google.auth.external_account_authorized_user.Credentials: The
@@ -118,7 +123,7 @@ def __init__(
118123 self .token = token
119124 self .expiry = expiry
120125 self ._audience = audience
121- self ._refresh_token = refresh_token
126+ self ._refresh_token_val = refresh_token
122127 self ._token_url = token_url
123128 self ._token_info_url = token_info_url
124129 self ._client_id = client_id
@@ -128,6 +133,7 @@ def __init__(
128133 self ._scopes = scopes
129134 self ._universe_domain = universe_domain or credentials .DEFAULT_UNIVERSE_DOMAIN
130135 self ._cred_file_path = None
136+ self ._trust_boundary = trust_boundary
131137
132138 if not self .valid and not self .can_refresh :
133139 raise exceptions .InvalidOperation (
@@ -164,7 +170,7 @@ def info(self):
164170 def constructor_args (self ):
165171 return {
166172 "audience" : self ._audience ,
167- "refresh_token" : self ._refresh_token ,
173+ "refresh_token" : self ._refresh_token_val ,
168174 "token_url" : self ._token_url ,
169175 "token_info_url" : self ._token_info_url ,
170176 "client_id" : self ._client_id ,
@@ -175,6 +181,7 @@ def constructor_args(self):
175181 "scopes" : self ._scopes ,
176182 "quota_project_id" : self ._quota_project_id ,
177183 "universe_domain" : self ._universe_domain ,
184+ "trust_boundary" : self ._trust_boundary ,
178185 }
179186
180187 @property
@@ -184,7 +191,7 @@ def scopes(self):
184191
185192 @property
186193 def requires_scopes (self ):
187- """ False: OAuth 2.0 credentials have their scopes set when
194+ """False: OAuth 2.0 credentials have their scopes set when
188195 the initial token is requested and can not be changed."""
189196 return False
190197
@@ -201,13 +208,13 @@ def client_secret(self):
201208 @property
202209 def audience (self ):
203210 """Optional[str]: The STS audience which contains the resource name for the
204- workforce pool and the provider identifier in that pool."""
211+ workforce pool and the provider identifier in that pool."""
205212 return self ._audience
206213
207214 @property
208215 def refresh_token (self ):
209216 """Optional[str]: The OAuth 2.0 refresh token."""
210- return self ._refresh_token
217+ return self ._refresh_token_val
211218
212219 @property
213220 def token_url (self ):
@@ -226,13 +233,18 @@ def revoke_url(self):
226233
227234 @property
228235 def is_user (self ):
229- """ True: This credential always represents a user."""
236+ """True: This credential always represents a user."""
230237 return True
231238
232239 @property
233240 def can_refresh (self ):
234241 return all (
235- (self ._refresh_token , self ._token_url , self ._client_id , self ._client_secret )
242+ (
243+ self ._refresh_token_val ,
244+ self ._token_url ,
245+ self ._client_id ,
246+ self ._client_secret ,
247+ )
236248 )
237249
238250 def get_project_id (self , request = None ):
@@ -266,7 +278,7 @@ def to_json(self, strip=None):
266278 strip = strip if strip else []
267279 return json .dumps ({k : v for (k , v ) in self .info .items () if k not in strip })
268280
269- def refresh (self , request ):
281+ def _refresh_token (self , request ):
270282 """Refreshes the access token.
271283
272284 Args:
@@ -285,18 +297,29 @@ def refresh(self, request):
285297 )
286298
287299 now = _helpers .utcnow ()
288- response_data = self ._make_sts_request (request )
300+ response_data = self ._sts_client . refresh_token (request , self . _refresh_token_val )
289301
290302 self .token = response_data .get ("access_token" )
291303
292304 lifetime = datetime .timedelta (seconds = response_data .get ("expires_in" ))
293305 self .expiry = now + lifetime
294306
295307 if "refresh_token" in response_data :
296- self ._refresh_token = response_data ["refresh_token" ]
308+ self ._refresh_token_val = response_data ["refresh_token" ]
309+
310+ def _build_trust_boundary_lookup_url (self ):
311+ """Builds and returns the URL for the trust boundary lookup API."""
312+ # Audience format: //iam.googleapis.com/locations/global/workforcePools/POOL_ID/providers/PROVIDER_ID
313+ match = re .search (r"locations/[^/]+/workforcePools/([^/]+)" , self ._audience )
314+
315+ if not match :
316+ raise exceptions .InvalidValue ("Invalid workforce pool audience format." )
317+
318+ pool_id = match .groups ()[0 ]
297319
298- def _make_sts_request (self , request ):
299- return self ._sts_client .refresh_token (request , self ._refresh_token )
320+ return _constants ._WORKFORCE_POOL_TRUST_BOUNDARY_LOOKUP_ENDPOINT .format (
321+ universe_domain = self ._universe_domain , pool_id = pool_id
322+ )
300323
301324 @_helpers .copy_docstring (credentials .Credentials )
302325 def get_cred_info (self ):
@@ -331,6 +354,12 @@ def with_universe_domain(self, universe_domain):
331354 cred ._universe_domain = universe_domain
332355 return cred
333356
357+ @_helpers .copy_docstring (credentials .CredentialsWithTrustBoundary )
358+ def with_trust_boundary (self , trust_boundary ):
359+ cred = self ._make_copy ()
360+ cred ._trust_boundary = trust_boundary
361+ return cred
362+
334363 @classmethod
335364 def from_info (cls , info , ** kwargs ):
336365 """Creates a Credentials instance from parsed external account info.
@@ -375,6 +404,7 @@ def from_info(cls, info, **kwargs):
375404 universe_domain = info .get (
376405 "universe_domain" , credentials .DEFAULT_UNIVERSE_DOMAIN
377406 ),
407+ trust_boundary = info .get ("trust_boundary" ),
378408 ** kwargs
379409 )
380410
0 commit comments