blob: e259f78256a85c48630a513659a55327e74c0408 [file] [log] [blame]
C.J. Collier37141e42020-02-13 13:49:49 -08001# Copyright 2016 Google LLC
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -07002#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""OAuth 2.0 Credentials.
16
17This module provides credentials based on OAuth 2.0 access and refresh tokens.
18These credentials usually access resources on behalf of a user (resource
19owner).
20
21Specifically, this is intended to use access tokens acquired using the
22`Authorization Code grant`_ and can refresh those tokens using a
23optional `refresh token`_.
24
25Obtaining the initial access and refresh token is outside of the scope of this
26module. Consult `rfc6749 section 4.1`_ for complete details on the
27Authorization Code grant flow.
28
29.. _Authorization Code grant: https://tools.ietf.org/html/rfc6749#section-1.3.1
30.. _refresh token: https://tools.ietf.org/html/rfc6749#section-6
31.. _rfc6749 section 4.1: https://tools.ietf.org/html/rfc6749#section-4.1
32"""
33
wesley chund0e0aba2020-09-17 09:18:55 -070034from datetime import datetime
Hiranya Jayathilaka23c88f72017-12-05 09:29:59 -080035import io
36import json
37
arithmetic1728772dac62020-03-27 14:34:13 -070038from google.auth import _cloud_sdk
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -070039from google.auth import _helpers
40from google.auth import credentials
Thea Flowers118c0482018-05-24 13:34:07 -070041from google.auth import exceptions
arithmetic172882293fe2021-04-14 11:22:13 -070042from google.oauth2 import reauth
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -070043
44
Hiranya Jayathilaka23c88f72017-12-05 09:29:59 -080045# The Google OAuth 2.0 token endpoint. Used for authorized user credentials.
Bu Sun Kim9eec0912019-10-21 17:04:21 -070046_GOOGLE_OAUTH2_TOKEN_ENDPOINT = "https://oauth2.googleapis.com/token"
Hiranya Jayathilaka23c88f72017-12-05 09:29:59 -080047
48
Bu Sun Kim41599ae2020-09-02 12:55:42 -060049class Credentials(credentials.ReadOnlyScoped, credentials.CredentialsWithQuotaProject):
Bu Sun Kimb12488c2020-06-10 13:44:07 -070050 """Credentials using OAuth 2.0 access and refresh tokens.
51
52 The credentials are considered immutable. If you want to modify the
53 quota project, use :meth:`with_quota_project` or ::
54
55 credentials = credentials.with_quota_project('myproject-123)
arithmetic172882293fe2021-04-14 11:22:13 -070056
arithmetic172813aed5f2021-09-07 16:24:45 -070057 Reauth is disabled by default. To enable reauth, set the
58 `enable_reauth_refresh` parameter to True in the constructor. Note that
59 reauth feature is intended for gcloud to use only.
arithmetic172882293fe2021-04-14 11:22:13 -070060 If reauth is enabled, `pyu2f` dependency has to be installed in order to use security
61 key reauth feature. Dependency can be installed via `pip install pyu2f` or `pip install
62 google-auth[reauth]`.
Bu Sun Kimb12488c2020-06-10 13:44:07 -070063 """
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -070064
Bu Sun Kim9eec0912019-10-21 17:04:21 -070065 def __init__(
66 self,
67 token,
68 refresh_token=None,
69 id_token=None,
70 token_uri=None,
71 client_id=None,
72 client_secret=None,
73 scopes=None,
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -070074 default_scopes=None,
Bu Sun Kim32d71a52019-12-18 11:30:46 -080075 quota_project_id=None,
wesley chund0e0aba2020-09-17 09:18:55 -070076 expiry=None,
arithmetic172882293fe2021-04-14 11:22:13 -070077 rapt_token=None,
bojeil-googleec2fb182021-07-22 10:01:31 -070078 refresh_handler=None,
arithmetic172813aed5f2021-09-07 16:24:45 -070079 enable_reauth_refresh=False,
Bu Sun Kim9eec0912019-10-21 17:04:21 -070080 ):
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -070081 """
82 Args:
83 token (Optional(str)): The OAuth 2.0 access token. Can be None
84 if refresh information is provided.
85 refresh_token (str): The OAuth 2.0 refresh token. If specified,
86 credentials can be refreshed.
Jon Wayne Parrott26a16372017-03-28 13:03:33 -070087 id_token (str): The Open ID Connect ID Token.
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -070088 token_uri (str): The OAuth 2.0 authorization server's token
89 endpoint URI. Must be specified for refresh, can be left as
90 None if the token can not be refreshed.
91 client_id (str): The OAuth 2.0 client ID. Must be specified for
92 refresh, can be left as None if the token can not be refreshed.
93 client_secret(str): The OAuth 2.0 client secret. Must be specified
94 for refresh, can be left as None if the token can not be
95 refreshed.
Eugene W. Foley49a18c42019-05-22 13:50:38 -040096 scopes (Sequence[str]): The scopes used to obtain authorization.
97 This parameter is used by :meth:`has_scopes`. OAuth 2.0
98 credentials can not request additional scopes after
99 authorization. The scopes must be derivable from the refresh
100 token if refresh information is provided (e.g. The refresh
101 token scopes are a superset of this or contain a wild card
102 scope like 'https://www.googleapis.com/auth/any-api').
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700103 default_scopes (Sequence[str]): Default scopes passed by a
104 Google client library. Use 'scopes' for user-defined scopes.
Bu Sun Kim32d71a52019-12-18 11:30:46 -0800105 quota_project_id (Optional[str]): The project ID used for quota and billing.
106 This project may be different from the project used to
107 create the credentials.
arithmetic172882293fe2021-04-14 11:22:13 -0700108 rapt_token (Optional[str]): The reauth Proof Token.
bojeil-googleec2fb182021-07-22 10:01:31 -0700109 refresh_handler (Optional[Callable[[google.auth.transport.Request, Sequence[str]], [str, datetime]]]):
110 A callable which takes in the HTTP request callable and the list of
111 OAuth scopes and when called returns an access token string for the
112 requested scopes and its expiry datetime. This is useful when no
113 refresh tokens are provided and tokens are obtained by calling
114 some external process on demand. It is particularly useful for
115 retrieving downscoped tokens from a token broker.
arithmetic172813aed5f2021-09-07 16:24:45 -0700116 enable_reauth_refresh (Optional[bool]): Whether reauth refresh flow
117 should be used. This flag is for gcloud to use only.
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -0700118 """
119 super(Credentials, self).__init__()
120 self.token = token
wesley chund0e0aba2020-09-17 09:18:55 -0700121 self.expiry = expiry
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -0700122 self._refresh_token = refresh_token
Jon Wayne Parrott26a16372017-03-28 13:03:33 -0700123 self._id_token = id_token
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -0700124 self._scopes = scopes
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700125 self._default_scopes = default_scopes
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -0700126 self._token_uri = token_uri
127 self._client_id = client_id
128 self._client_secret = client_secret
Bu Sun Kim32d71a52019-12-18 11:30:46 -0800129 self._quota_project_id = quota_project_id
arithmetic172882293fe2021-04-14 11:22:13 -0700130 self._rapt_token = rapt_token
bojeil-googleec2fb182021-07-22 10:01:31 -0700131 self.refresh_handler = refresh_handler
arithmetic172813aed5f2021-09-07 16:24:45 -0700132 self._enable_reauth_refresh = enable_reauth_refresh
Bu Sun Kim32d71a52019-12-18 11:30:46 -0800133
134 def __getstate__(self):
135 """A __getstate__ method must exist for the __setstate__ to be called
136 This is identical to the default implementation.
137 See https://docs.python.org/3.7/library/pickle.html#object.__setstate__
138 """
bojeil-googleec2fb182021-07-22 10:01:31 -0700139 state_dict = self.__dict__.copy()
140 # Remove _refresh_handler function as there are limitations pickling and
141 # unpickling certain callables (lambda, functools.partial instances)
142 # because they need to be importable.
143 # Instead, the refresh_handler setter should be used to repopulate this.
144 del state_dict["_refresh_handler"]
145 return state_dict
Bu Sun Kim32d71a52019-12-18 11:30:46 -0800146
147 def __setstate__(self, d):
148 """Credentials pickled with older versions of the class do not have
149 all the attributes."""
150 self.token = d.get("token")
151 self.expiry = d.get("expiry")
152 self._refresh_token = d.get("_refresh_token")
153 self._id_token = d.get("_id_token")
154 self._scopes = d.get("_scopes")
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700155 self._default_scopes = d.get("_default_scopes")
Bu Sun Kim32d71a52019-12-18 11:30:46 -0800156 self._token_uri = d.get("_token_uri")
157 self._client_id = d.get("_client_id")
158 self._client_secret = d.get("_client_secret")
159 self._quota_project_id = d.get("_quota_project_id")
arithmetic172882293fe2021-04-14 11:22:13 -0700160 self._rapt_token = d.get("_rapt_token")
arithmetic172813aed5f2021-09-07 16:24:45 -0700161 self._enable_reauth_refresh = d.get("_enable_reauth_refresh")
bojeil-googleec2fb182021-07-22 10:01:31 -0700162 # The refresh_handler setter should be used to repopulate this.
163 self._refresh_handler = None
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -0700164
165 @property
Jon Wayne Parrott2d0549a2017-03-01 09:27:16 -0800166 def refresh_token(self):
167 """Optional[str]: The OAuth 2.0 refresh token."""
168 return self._refresh_token
169
170 @property
wesley chund0e0aba2020-09-17 09:18:55 -0700171 def scopes(self):
172 """Optional[str]: The OAuth 2.0 permission scopes."""
173 return self._scopes
174
175 @property
Jon Wayne Parrott2d0549a2017-03-01 09:27:16 -0800176 def token_uri(self):
177 """Optional[str]: The OAuth 2.0 authorization server's token endpoint
178 URI."""
179 return self._token_uri
180
181 @property
Jon Wayne Parrott26a16372017-03-28 13:03:33 -0700182 def id_token(self):
183 """Optional[str]: The Open ID Connect ID Token.
184
185 Depending on the authorization server and the scopes requested, this
186 may be populated when credentials are obtained and updated when
187 :meth:`refresh` is called. This token is a JWT. It can be verified
188 and decoded using :func:`google.oauth2.id_token.verify_oauth2_token`.
189 """
190 return self._id_token
191
192 @property
Jon Wayne Parrott2d0549a2017-03-01 09:27:16 -0800193 def client_id(self):
194 """Optional[str]: The OAuth 2.0 client ID."""
195 return self._client_id
196
197 @property
198 def client_secret(self):
199 """Optional[str]: The OAuth 2.0 client secret."""
200 return self._client_secret
201
202 @property
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -0700203 def requires_scopes(self):
204 """False: OAuth 2.0 credentials have their scopes set when
205 the initial token is requested and can not be changed."""
206 return False
207
arithmetic172882293fe2021-04-14 11:22:13 -0700208 @property
209 def rapt_token(self):
210 """Optional[str]: The reauth Proof Token."""
211 return self._rapt_token
212
bojeil-googleec2fb182021-07-22 10:01:31 -0700213 @property
214 def refresh_handler(self):
215 """Returns the refresh handler if available.
216
217 Returns:
218 Optional[Callable[[google.auth.transport.Request, Sequence[str]], [str, datetime]]]:
219 The current refresh handler.
220 """
221 return self._refresh_handler
222
223 @refresh_handler.setter
224 def refresh_handler(self, value):
225 """Updates the current refresh handler.
226
227 Args:
228 value (Optional[Callable[[google.auth.transport.Request, Sequence[str]], [str, datetime]]]):
229 The updated value of the refresh handler.
230
231 Raises:
232 TypeError: If the value is not a callable or None.
233 """
234 if not callable(value) and value is not None:
235 raise TypeError("The provided refresh_handler is not a callable or None.")
236 self._refresh_handler = value
237
Bu Sun Kim41599ae2020-09-02 12:55:42 -0600238 @_helpers.copy_docstring(credentials.CredentialsWithQuotaProject)
Bu Sun Kimb12488c2020-06-10 13:44:07 -0700239 def with_quota_project(self, quota_project_id):
Bu Sun Kimb12488c2020-06-10 13:44:07 -0700240
Bu Sun Kimb12488c2020-06-10 13:44:07 -0700241 return self.__class__(
242 self.token,
243 refresh_token=self.refresh_token,
244 id_token=self.id_token,
245 token_uri=self.token_uri,
246 client_id=self.client_id,
247 client_secret=self.client_secret,
248 scopes=self.scopes,
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700249 default_scopes=self.default_scopes,
Bu Sun Kimb12488c2020-06-10 13:44:07 -0700250 quota_project_id=quota_project_id,
arithmetic172882293fe2021-04-14 11:22:13 -0700251 rapt_token=self.rapt_token,
arithmetic172813aed5f2021-09-07 16:24:45 -0700252 enable_reauth_refresh=self._enable_reauth_refresh,
Bu Sun Kimb12488c2020-06-10 13:44:07 -0700253 )
254
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -0700255 @_helpers.copy_docstring(credentials.Credentials)
256 def refresh(self, request):
bojeil-googleec2fb182021-07-22 10:01:31 -0700257 scopes = self._scopes if self._scopes is not None else self._default_scopes
258 # Use refresh handler if available and no refresh token is
259 # available. This is useful in general when tokens are obtained by calling
260 # some external process on demand. It is particularly useful for retrieving
261 # downscoped tokens from a token broker.
262 if self._refresh_token is None and self.refresh_handler:
263 token, expiry = self.refresh_handler(request, scopes=scopes)
264 # Validate returned data.
265 if not isinstance(token, str):
266 raise exceptions.RefreshError(
267 "The refresh_handler returned token is not a string."
268 )
269 if not isinstance(expiry, datetime):
270 raise exceptions.RefreshError(
271 "The refresh_handler returned expiry is not a datetime object."
272 )
273 if _helpers.utcnow() >= expiry - _helpers.CLOCK_SKEW:
274 raise exceptions.RefreshError(
275 "The credentials returned by the refresh_handler are "
276 "already expired."
277 )
278 self.token = token
279 self.expiry = expiry
280 return
281
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700282 if (
283 self._refresh_token is None
284 or self._token_uri is None
285 or self._client_id is None
286 or self._client_secret is None
287 ):
Thea Flowers118c0482018-05-24 13:34:07 -0700288 raise exceptions.RefreshError(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700289 "The credentials do not contain the necessary fields need to "
290 "refresh the access token. You must specify refresh_token, "
291 "token_uri, client_id, and client_secret."
292 )
Thea Flowers118c0482018-05-24 13:34:07 -0700293
arithmetic172882293fe2021-04-14 11:22:13 -0700294 (
295 access_token,
296 refresh_token,
297 expiry,
298 grant_response,
299 rapt_token,
300 ) = reauth.refresh_grant(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700301 request,
302 self._token_uri,
303 self._refresh_token,
304 self._client_id,
305 self._client_secret,
arithmetic172882293fe2021-04-14 11:22:13 -0700306 scopes=scopes,
307 rapt_token=self._rapt_token,
arithmetic172813aed5f2021-09-07 16:24:45 -0700308 enable_reauth_refresh=self._enable_reauth_refresh,
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700309 )
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -0700310
311 self.token = access_token
312 self.expiry = expiry
313 self._refresh_token = refresh_token
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700314 self._id_token = grant_response.get("id_token")
arithmetic172882293fe2021-04-14 11:22:13 -0700315 self._rapt_token = rapt_token
Hiranya Jayathilaka23c88f72017-12-05 09:29:59 -0800316
arithmetic172882293fe2021-04-14 11:22:13 -0700317 if scopes and "scope" in grant_response:
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700318 requested_scopes = frozenset(scopes)
arithmetic172882293fe2021-04-14 11:22:13 -0700319 granted_scopes = frozenset(grant_response["scope"].split())
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700320 scopes_requested_but_not_granted = requested_scopes - granted_scopes
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400321 if scopes_requested_but_not_granted:
322 raise exceptions.RefreshError(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700323 "Not all requested scopes were granted by the "
324 "authorization server, missing scopes {}.".format(
325 ", ".join(scopes_requested_but_not_granted)
326 )
327 )
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400328
Hiranya Jayathilaka23c88f72017-12-05 09:29:59 -0800329 @classmethod
330 def from_authorized_user_info(cls, info, scopes=None):
331 """Creates a Credentials instance from parsed authorized user info.
332
333 Args:
334 info (Mapping[str, str]): The authorized user info in Google
335 format.
336 scopes (Sequence[str]): Optional list of scopes to include in the
337 credentials.
338
339 Returns:
340 google.oauth2.credentials.Credentials: The constructed
341 credentials.
342
343 Raises:
344 ValueError: If the info is not in the expected format.
345 """
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700346 keys_needed = set(("refresh_token", "client_id", "client_secret"))
Tres Seaver560cf1e2021-08-03 16:35:54 -0400347 missing = keys_needed.difference(info)
Hiranya Jayathilaka23c88f72017-12-05 09:29:59 -0800348
349 if missing:
350 raise ValueError(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700351 "Authorized user info was not in the expected format, missing "
352 "fields {}.".format(", ".join(missing))
353 )
Hiranya Jayathilaka23c88f72017-12-05 09:29:59 -0800354
wesley chund0e0aba2020-09-17 09:18:55 -0700355 # access token expiry (datetime obj); auto-expire if not saved
356 expiry = info.get("expiry")
357 if expiry:
358 expiry = datetime.strptime(
359 expiry.rstrip("Z").split(".")[0], "%Y-%m-%dT%H:%M:%S"
360 )
361 else:
362 expiry = _helpers.utcnow() - _helpers.CLOCK_SKEW
363
364 # process scopes, which needs to be a seq
365 if scopes is None and "scopes" in info:
366 scopes = info.get("scopes")
367 if isinstance(scopes, str):
368 scopes = scopes.split(" ")
369
Emile Caron530f5f92019-07-26 01:23:25 +0200370 return cls(
wesley chund0e0aba2020-09-17 09:18:55 -0700371 token=info.get("token"),
372 refresh_token=info.get("refresh_token"),
373 token_uri=_GOOGLE_OAUTH2_TOKEN_ENDPOINT, # always overrides
Hiranya Jayathilaka23c88f72017-12-05 09:29:59 -0800374 scopes=scopes,
wesley chund0e0aba2020-09-17 09:18:55 -0700375 client_id=info.get("client_id"),
376 client_secret=info.get("client_secret"),
377 quota_project_id=info.get("quota_project_id"), # may not exist
378 expiry=expiry,
arithmetic172813aed5f2021-09-07 16:24:45 -0700379 rapt_token=info.get("rapt_token"), # may not exist
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700380 )
Hiranya Jayathilaka23c88f72017-12-05 09:29:59 -0800381
382 @classmethod
383 def from_authorized_user_file(cls, filename, scopes=None):
384 """Creates a Credentials instance from an authorized user json file.
385
386 Args:
387 filename (str): The path to the authorized user json file.
388 scopes (Sequence[str]): Optional list of scopes to include in the
389 credentials.
390
391 Returns:
392 google.oauth2.credentials.Credentials: The constructed
393 credentials.
394
395 Raises:
396 ValueError: If the file is not in the expected format.
397 """
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700398 with io.open(filename, "r", encoding="utf-8") as json_file:
Hiranya Jayathilaka23c88f72017-12-05 09:29:59 -0800399 data = json.load(json_file)
400 return cls.from_authorized_user_info(data, scopes)
patkasperbfb1f8c2019-12-05 22:03:44 +0100401
402 def to_json(self, strip=None):
403 """Utility function that creates a JSON representation of a Credentials
404 object.
405
406 Args:
407 strip (Sequence[str]): Optional list of members to exclude from the
408 generated JSON.
409
410 Returns:
alvyjudy67d38d82020-06-10 22:23:31 -0400411 str: A JSON representation of this instance. When converted into
412 a dictionary, it can be passed to from_authorized_user_info()
413 to create a new credential instance.
patkasperbfb1f8c2019-12-05 22:03:44 +0100414 """
415 prep = {
416 "token": self.token,
417 "refresh_token": self.refresh_token,
418 "token_uri": self.token_uri,
419 "client_id": self.client_id,
420 "client_secret": self.client_secret,
421 "scopes": self.scopes,
arithmetic172882293fe2021-04-14 11:22:13 -0700422 "rapt_token": self.rapt_token,
patkasperbfb1f8c2019-12-05 22:03:44 +0100423 }
wesley chund0e0aba2020-09-17 09:18:55 -0700424 if self.expiry: # flatten expiry timestamp
425 prep["expiry"] = self.expiry.isoformat() + "Z"
patkasperbfb1f8c2019-12-05 22:03:44 +0100426
wesley chund0e0aba2020-09-17 09:18:55 -0700427 # Remove empty entries (those which are None)
patkasperbfb1f8c2019-12-05 22:03:44 +0100428 prep = {k: v for k, v in prep.items() if v is not None}
429
430 # Remove entries that explicitely need to be removed
431 if strip is not None:
432 prep = {k: v for k, v in prep.items() if k not in strip}
433
434 return json.dumps(prep)
arithmetic1728772dac62020-03-27 14:34:13 -0700435
436
Bu Sun Kim41599ae2020-09-02 12:55:42 -0600437class UserAccessTokenCredentials(credentials.CredentialsWithQuotaProject):
arithmetic1728772dac62020-03-27 14:34:13 -0700438 """Access token credentials for user account.
439
440 Obtain the access token for a given user account or the current active
441 user account with the ``gcloud auth print-access-token`` command.
442
443 Args:
444 account (Optional[str]): Account to get the access token for. If not
445 specified, the current active account will be used.
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700446 quota_project_id (Optional[str]): The project ID used for quota
447 and billing.
arithmetic1728772dac62020-03-27 14:34:13 -0700448 """
449
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700450 def __init__(self, account=None, quota_project_id=None):
arithmetic1728772dac62020-03-27 14:34:13 -0700451 super(UserAccessTokenCredentials, self).__init__()
452 self._account = account
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700453 self._quota_project_id = quota_project_id
arithmetic1728772dac62020-03-27 14:34:13 -0700454
455 def with_account(self, account):
456 """Create a new instance with the given account.
457
458 Args:
459 account (str): Account to get the access token for.
460
461 Returns:
462 google.oauth2.credentials.UserAccessTokenCredentials: The created
463 credentials with the given account.
464 """
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700465 return self.__class__(account=account, quota_project_id=self._quota_project_id)
466
Bu Sun Kim41599ae2020-09-02 12:55:42 -0600467 @_helpers.copy_docstring(credentials.CredentialsWithQuotaProject)
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700468 def with_quota_project(self, quota_project_id):
469 return self.__class__(account=self._account, quota_project_id=quota_project_id)
arithmetic1728772dac62020-03-27 14:34:13 -0700470
471 def refresh(self, request):
472 """Refreshes the access token.
473
474 Args:
475 request (google.auth.transport.Request): This argument is required
476 by the base class interface but not used in this implementation,
477 so just set it to `None`.
478
479 Raises:
480 google.auth.exceptions.UserAccessTokenError: If the access token
481 refresh failed.
482 """
483 self.token = _cloud_sdk.get_auth_access_token(self._account)
484
485 @_helpers.copy_docstring(credentials.Credentials)
486 def before_request(self, request, method, url, headers):
487 self.refresh(request)
488 self.apply(headers)