blob: 9b59f8cf532fa1736f312f12c20fc9acafce8f7e [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
arithmetic17285bd5ccf2021-10-21 15:25:46 -070038import six
39
arithmetic1728772dac62020-03-27 14:34:13 -070040from google.auth import _cloud_sdk
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -070041from google.auth import _helpers
42from google.auth import credentials
Thea Flowers118c0482018-05-24 13:34:07 -070043from google.auth import exceptions
arithmetic172882293fe2021-04-14 11:22:13 -070044from google.oauth2 import reauth
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -070045
46
Hiranya Jayathilaka23c88f72017-12-05 09:29:59 -080047# The Google OAuth 2.0 token endpoint. Used for authorized user credentials.
Bu Sun Kim9eec0912019-10-21 17:04:21 -070048_GOOGLE_OAUTH2_TOKEN_ENDPOINT = "https://oauth2.googleapis.com/token"
Hiranya Jayathilaka23c88f72017-12-05 09:29:59 -080049
50
Bu Sun Kim41599ae2020-09-02 12:55:42 -060051class Credentials(credentials.ReadOnlyScoped, credentials.CredentialsWithQuotaProject):
Bu Sun Kimb12488c2020-06-10 13:44:07 -070052 """Credentials using OAuth 2.0 access and refresh tokens.
53
54 The credentials are considered immutable. If you want to modify the
55 quota project, use :meth:`with_quota_project` or ::
56
57 credentials = credentials.with_quota_project('myproject-123)
arithmetic172882293fe2021-04-14 11:22:13 -070058
arithmetic172813aed5f2021-09-07 16:24:45 -070059 Reauth is disabled by default. To enable reauth, set the
60 `enable_reauth_refresh` parameter to True in the constructor. Note that
61 reauth feature is intended for gcloud to use only.
arithmetic172882293fe2021-04-14 11:22:13 -070062 If reauth is enabled, `pyu2f` dependency has to be installed in order to use security
63 key reauth feature. Dependency can be installed via `pip install pyu2f` or `pip install
64 google-auth[reauth]`.
Bu Sun Kimb12488c2020-06-10 13:44:07 -070065 """
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -070066
Bu Sun Kim9eec0912019-10-21 17:04:21 -070067 def __init__(
68 self,
69 token,
70 refresh_token=None,
71 id_token=None,
72 token_uri=None,
73 client_id=None,
74 client_secret=None,
75 scopes=None,
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -070076 default_scopes=None,
Bu Sun Kim32d71a52019-12-18 11:30:46 -080077 quota_project_id=None,
wesley chund0e0aba2020-09-17 09:18:55 -070078 expiry=None,
arithmetic172882293fe2021-04-14 11:22:13 -070079 rapt_token=None,
bojeil-googleec2fb182021-07-22 10:01:31 -070080 refresh_handler=None,
arithmetic172813aed5f2021-09-07 16:24:45 -070081 enable_reauth_refresh=False,
Bu Sun Kim9eec0912019-10-21 17:04:21 -070082 ):
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -070083 """
84 Args:
85 token (Optional(str)): The OAuth 2.0 access token. Can be None
86 if refresh information is provided.
87 refresh_token (str): The OAuth 2.0 refresh token. If specified,
88 credentials can be refreshed.
Jon Wayne Parrott26a16372017-03-28 13:03:33 -070089 id_token (str): The Open ID Connect ID Token.
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -070090 token_uri (str): The OAuth 2.0 authorization server's token
91 endpoint URI. Must be specified for refresh, can be left as
92 None if the token can not be refreshed.
93 client_id (str): The OAuth 2.0 client ID. Must be specified for
94 refresh, can be left as None if the token can not be refreshed.
95 client_secret(str): The OAuth 2.0 client secret. Must be specified
96 for refresh, can be left as None if the token can not be
97 refreshed.
Eugene W. Foley49a18c42019-05-22 13:50:38 -040098 scopes (Sequence[str]): The scopes used to obtain authorization.
99 This parameter is used by :meth:`has_scopes`. OAuth 2.0
100 credentials can not request additional scopes after
101 authorization. The scopes must be derivable from the refresh
102 token if refresh information is provided (e.g. The refresh
103 token scopes are a superset of this or contain a wild card
104 scope like 'https://www.googleapis.com/auth/any-api').
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700105 default_scopes (Sequence[str]): Default scopes passed by a
106 Google client library. Use 'scopes' for user-defined scopes.
Bu Sun Kim32d71a52019-12-18 11:30:46 -0800107 quota_project_id (Optional[str]): The project ID used for quota and billing.
108 This project may be different from the project used to
109 create the credentials.
arithmetic172882293fe2021-04-14 11:22:13 -0700110 rapt_token (Optional[str]): The reauth Proof Token.
bojeil-googleec2fb182021-07-22 10:01:31 -0700111 refresh_handler (Optional[Callable[[google.auth.transport.Request, Sequence[str]], [str, datetime]]]):
112 A callable which takes in the HTTP request callable and the list of
113 OAuth scopes and when called returns an access token string for the
114 requested scopes and its expiry datetime. This is useful when no
115 refresh tokens are provided and tokens are obtained by calling
116 some external process on demand. It is particularly useful for
117 retrieving downscoped tokens from a token broker.
arithmetic172813aed5f2021-09-07 16:24:45 -0700118 enable_reauth_refresh (Optional[bool]): Whether reauth refresh flow
119 should be used. This flag is for gcloud to use only.
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -0700120 """
121 super(Credentials, self).__init__()
122 self.token = token
wesley chund0e0aba2020-09-17 09:18:55 -0700123 self.expiry = expiry
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -0700124 self._refresh_token = refresh_token
Jon Wayne Parrott26a16372017-03-28 13:03:33 -0700125 self._id_token = id_token
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -0700126 self._scopes = scopes
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700127 self._default_scopes = default_scopes
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -0700128 self._token_uri = token_uri
129 self._client_id = client_id
130 self._client_secret = client_secret
Bu Sun Kim32d71a52019-12-18 11:30:46 -0800131 self._quota_project_id = quota_project_id
arithmetic172882293fe2021-04-14 11:22:13 -0700132 self._rapt_token = rapt_token
bojeil-googleec2fb182021-07-22 10:01:31 -0700133 self.refresh_handler = refresh_handler
arithmetic172813aed5f2021-09-07 16:24:45 -0700134 self._enable_reauth_refresh = enable_reauth_refresh
Bu Sun Kim32d71a52019-12-18 11:30:46 -0800135
136 def __getstate__(self):
137 """A __getstate__ method must exist for the __setstate__ to be called
138 This is identical to the default implementation.
139 See https://docs.python.org/3.7/library/pickle.html#object.__setstate__
140 """
bojeil-googleec2fb182021-07-22 10:01:31 -0700141 state_dict = self.__dict__.copy()
142 # Remove _refresh_handler function as there are limitations pickling and
143 # unpickling certain callables (lambda, functools.partial instances)
144 # because they need to be importable.
145 # Instead, the refresh_handler setter should be used to repopulate this.
146 del state_dict["_refresh_handler"]
147 return state_dict
Bu Sun Kim32d71a52019-12-18 11:30:46 -0800148
149 def __setstate__(self, d):
150 """Credentials pickled with older versions of the class do not have
151 all the attributes."""
152 self.token = d.get("token")
153 self.expiry = d.get("expiry")
154 self._refresh_token = d.get("_refresh_token")
155 self._id_token = d.get("_id_token")
156 self._scopes = d.get("_scopes")
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700157 self._default_scopes = d.get("_default_scopes")
Bu Sun Kim32d71a52019-12-18 11:30:46 -0800158 self._token_uri = d.get("_token_uri")
159 self._client_id = d.get("_client_id")
160 self._client_secret = d.get("_client_secret")
161 self._quota_project_id = d.get("_quota_project_id")
arithmetic172882293fe2021-04-14 11:22:13 -0700162 self._rapt_token = d.get("_rapt_token")
arithmetic172813aed5f2021-09-07 16:24:45 -0700163 self._enable_reauth_refresh = d.get("_enable_reauth_refresh")
bojeil-googleec2fb182021-07-22 10:01:31 -0700164 # The refresh_handler setter should be used to repopulate this.
165 self._refresh_handler = None
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -0700166
167 @property
Jon Wayne Parrott2d0549a2017-03-01 09:27:16 -0800168 def refresh_token(self):
169 """Optional[str]: The OAuth 2.0 refresh token."""
170 return self._refresh_token
171
172 @property
wesley chund0e0aba2020-09-17 09:18:55 -0700173 def scopes(self):
174 """Optional[str]: The OAuth 2.0 permission scopes."""
175 return self._scopes
176
177 @property
Jon Wayne Parrott2d0549a2017-03-01 09:27:16 -0800178 def token_uri(self):
179 """Optional[str]: The OAuth 2.0 authorization server's token endpoint
180 URI."""
181 return self._token_uri
182
183 @property
Jon Wayne Parrott26a16372017-03-28 13:03:33 -0700184 def id_token(self):
185 """Optional[str]: The Open ID Connect ID Token.
186
187 Depending on the authorization server and the scopes requested, this
188 may be populated when credentials are obtained and updated when
189 :meth:`refresh` is called. This token is a JWT. It can be verified
190 and decoded using :func:`google.oauth2.id_token.verify_oauth2_token`.
191 """
192 return self._id_token
193
194 @property
Jon Wayne Parrott2d0549a2017-03-01 09:27:16 -0800195 def client_id(self):
196 """Optional[str]: The OAuth 2.0 client ID."""
197 return self._client_id
198
199 @property
200 def client_secret(self):
201 """Optional[str]: The OAuth 2.0 client secret."""
202 return self._client_secret
203
204 @property
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -0700205 def requires_scopes(self):
206 """False: OAuth 2.0 credentials have their scopes set when
207 the initial token is requested and can not be changed."""
208 return False
209
arithmetic172882293fe2021-04-14 11:22:13 -0700210 @property
211 def rapt_token(self):
212 """Optional[str]: The reauth Proof Token."""
213 return self._rapt_token
214
bojeil-googleec2fb182021-07-22 10:01:31 -0700215 @property
216 def refresh_handler(self):
217 """Returns the refresh handler if available.
218
219 Returns:
220 Optional[Callable[[google.auth.transport.Request, Sequence[str]], [str, datetime]]]:
221 The current refresh handler.
222 """
223 return self._refresh_handler
224
225 @refresh_handler.setter
226 def refresh_handler(self, value):
227 """Updates the current refresh handler.
228
229 Args:
230 value (Optional[Callable[[google.auth.transport.Request, Sequence[str]], [str, datetime]]]):
231 The updated value of the refresh handler.
232
233 Raises:
234 TypeError: If the value is not a callable or None.
235 """
236 if not callable(value) and value is not None:
237 raise TypeError("The provided refresh_handler is not a callable or None.")
238 self._refresh_handler = value
239
Bu Sun Kim41599ae2020-09-02 12:55:42 -0600240 @_helpers.copy_docstring(credentials.CredentialsWithQuotaProject)
Bu Sun Kimb12488c2020-06-10 13:44:07 -0700241 def with_quota_project(self, quota_project_id):
Bu Sun Kimb12488c2020-06-10 13:44:07 -0700242
Bu Sun Kimb12488c2020-06-10 13:44:07 -0700243 return self.__class__(
244 self.token,
245 refresh_token=self.refresh_token,
246 id_token=self.id_token,
247 token_uri=self.token_uri,
248 client_id=self.client_id,
249 client_secret=self.client_secret,
250 scopes=self.scopes,
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700251 default_scopes=self.default_scopes,
Bu Sun Kimb12488c2020-06-10 13:44:07 -0700252 quota_project_id=quota_project_id,
arithmetic172882293fe2021-04-14 11:22:13 -0700253 rapt_token=self.rapt_token,
arithmetic172813aed5f2021-09-07 16:24:45 -0700254 enable_reauth_refresh=self._enable_reauth_refresh,
Bu Sun Kimb12488c2020-06-10 13:44:07 -0700255 )
256
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -0700257 @_helpers.copy_docstring(credentials.Credentials)
258 def refresh(self, request):
bojeil-googleec2fb182021-07-22 10:01:31 -0700259 scopes = self._scopes if self._scopes is not None else self._default_scopes
260 # Use refresh handler if available and no refresh token is
261 # available. This is useful in general when tokens are obtained by calling
262 # some external process on demand. It is particularly useful for retrieving
263 # downscoped tokens from a token broker.
264 if self._refresh_token is None and self.refresh_handler:
265 token, expiry = self.refresh_handler(request, scopes=scopes)
266 # Validate returned data.
arithmetic17285bd5ccf2021-10-21 15:25:46 -0700267 if not isinstance(token, six.string_types):
bojeil-googleec2fb182021-07-22 10:01:31 -0700268 raise exceptions.RefreshError(
269 "The refresh_handler returned token is not a string."
270 )
271 if not isinstance(expiry, datetime):
272 raise exceptions.RefreshError(
273 "The refresh_handler returned expiry is not a datetime object."
274 )
arithmetic1728738611b2021-09-09 17:09:54 -0700275 if _helpers.utcnow() >= expiry - _helpers.REFRESH_THRESHOLD:
bojeil-googleec2fb182021-07-22 10:01:31 -0700276 raise exceptions.RefreshError(
277 "The credentials returned by the refresh_handler are "
278 "already expired."
279 )
280 self.token = token
281 self.expiry = expiry
282 return
283
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700284 if (
285 self._refresh_token is None
286 or self._token_uri is None
287 or self._client_id is None
288 or self._client_secret is None
289 ):
Thea Flowers118c0482018-05-24 13:34:07 -0700290 raise exceptions.RefreshError(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700291 "The credentials do not contain the necessary fields need to "
292 "refresh the access token. You must specify refresh_token, "
293 "token_uri, client_id, and client_secret."
294 )
Thea Flowers118c0482018-05-24 13:34:07 -0700295
arithmetic172882293fe2021-04-14 11:22:13 -0700296 (
297 access_token,
298 refresh_token,
299 expiry,
300 grant_response,
301 rapt_token,
302 ) = reauth.refresh_grant(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700303 request,
304 self._token_uri,
305 self._refresh_token,
306 self._client_id,
307 self._client_secret,
arithmetic172882293fe2021-04-14 11:22:13 -0700308 scopes=scopes,
309 rapt_token=self._rapt_token,
arithmetic172813aed5f2021-09-07 16:24:45 -0700310 enable_reauth_refresh=self._enable_reauth_refresh,
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700311 )
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -0700312
313 self.token = access_token
314 self.expiry = expiry
315 self._refresh_token = refresh_token
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700316 self._id_token = grant_response.get("id_token")
arithmetic172882293fe2021-04-14 11:22:13 -0700317 self._rapt_token = rapt_token
Hiranya Jayathilaka23c88f72017-12-05 09:29:59 -0800318
arithmetic172882293fe2021-04-14 11:22:13 -0700319 if scopes and "scope" in grant_response:
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700320 requested_scopes = frozenset(scopes)
arithmetic172882293fe2021-04-14 11:22:13 -0700321 granted_scopes = frozenset(grant_response["scope"].split())
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700322 scopes_requested_but_not_granted = requested_scopes - granted_scopes
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400323 if scopes_requested_but_not_granted:
324 raise exceptions.RefreshError(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700325 "Not all requested scopes were granted by the "
326 "authorization server, missing scopes {}.".format(
327 ", ".join(scopes_requested_but_not_granted)
328 )
329 )
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400330
Hiranya Jayathilaka23c88f72017-12-05 09:29:59 -0800331 @classmethod
332 def from_authorized_user_info(cls, info, scopes=None):
333 """Creates a Credentials instance from parsed authorized user info.
334
335 Args:
336 info (Mapping[str, str]): The authorized user info in Google
337 format.
338 scopes (Sequence[str]): Optional list of scopes to include in the
339 credentials.
340
341 Returns:
342 google.oauth2.credentials.Credentials: The constructed
343 credentials.
344
345 Raises:
346 ValueError: If the info is not in the expected format.
347 """
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700348 keys_needed = set(("refresh_token", "client_id", "client_secret"))
arithmetic17285bd5ccf2021-10-21 15:25:46 -0700349 missing = keys_needed.difference(six.iterkeys(info))
Hiranya Jayathilaka23c88f72017-12-05 09:29:59 -0800350
351 if missing:
352 raise ValueError(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700353 "Authorized user info was not in the expected format, missing "
354 "fields {}.".format(", ".join(missing))
355 )
Hiranya Jayathilaka23c88f72017-12-05 09:29:59 -0800356
wesley chund0e0aba2020-09-17 09:18:55 -0700357 # access token expiry (datetime obj); auto-expire if not saved
358 expiry = info.get("expiry")
359 if expiry:
360 expiry = datetime.strptime(
361 expiry.rstrip("Z").split(".")[0], "%Y-%m-%dT%H:%M:%S"
362 )
363 else:
arithmetic1728738611b2021-09-09 17:09:54 -0700364 expiry = _helpers.utcnow() - _helpers.REFRESH_THRESHOLD
wesley chund0e0aba2020-09-17 09:18:55 -0700365
366 # process scopes, which needs to be a seq
367 if scopes is None and "scopes" in info:
368 scopes = info.get("scopes")
arithmetic17285bd5ccf2021-10-21 15:25:46 -0700369 if isinstance(scopes, six.string_types):
wesley chund0e0aba2020-09-17 09:18:55 -0700370 scopes = scopes.split(" ")
371
Emile Caron530f5f92019-07-26 01:23:25 +0200372 return cls(
wesley chund0e0aba2020-09-17 09:18:55 -0700373 token=info.get("token"),
374 refresh_token=info.get("refresh_token"),
375 token_uri=_GOOGLE_OAUTH2_TOKEN_ENDPOINT, # always overrides
Hiranya Jayathilaka23c88f72017-12-05 09:29:59 -0800376 scopes=scopes,
wesley chund0e0aba2020-09-17 09:18:55 -0700377 client_id=info.get("client_id"),
378 client_secret=info.get("client_secret"),
379 quota_project_id=info.get("quota_project_id"), # may not exist
380 expiry=expiry,
arithmetic172813aed5f2021-09-07 16:24:45 -0700381 rapt_token=info.get("rapt_token"), # may not exist
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700382 )
Hiranya Jayathilaka23c88f72017-12-05 09:29:59 -0800383
384 @classmethod
385 def from_authorized_user_file(cls, filename, scopes=None):
386 """Creates a Credentials instance from an authorized user json file.
387
388 Args:
389 filename (str): The path to the authorized user json file.
390 scopes (Sequence[str]): Optional list of scopes to include in the
391 credentials.
392
393 Returns:
394 google.oauth2.credentials.Credentials: The constructed
395 credentials.
396
397 Raises:
398 ValueError: If the file is not in the expected format.
399 """
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700400 with io.open(filename, "r", encoding="utf-8") as json_file:
Hiranya Jayathilaka23c88f72017-12-05 09:29:59 -0800401 data = json.load(json_file)
402 return cls.from_authorized_user_info(data, scopes)
patkasperbfb1f8c2019-12-05 22:03:44 +0100403
404 def to_json(self, strip=None):
405 """Utility function that creates a JSON representation of a Credentials
406 object.
407
408 Args:
409 strip (Sequence[str]): Optional list of members to exclude from the
410 generated JSON.
411
412 Returns:
alvyjudy67d38d82020-06-10 22:23:31 -0400413 str: A JSON representation of this instance. When converted into
414 a dictionary, it can be passed to from_authorized_user_info()
415 to create a new credential instance.
patkasperbfb1f8c2019-12-05 22:03:44 +0100416 """
417 prep = {
418 "token": self.token,
419 "refresh_token": self.refresh_token,
420 "token_uri": self.token_uri,
421 "client_id": self.client_id,
422 "client_secret": self.client_secret,
423 "scopes": self.scopes,
arithmetic172882293fe2021-04-14 11:22:13 -0700424 "rapt_token": self.rapt_token,
patkasperbfb1f8c2019-12-05 22:03:44 +0100425 }
wesley chund0e0aba2020-09-17 09:18:55 -0700426 if self.expiry: # flatten expiry timestamp
427 prep["expiry"] = self.expiry.isoformat() + "Z"
patkasperbfb1f8c2019-12-05 22:03:44 +0100428
wesley chund0e0aba2020-09-17 09:18:55 -0700429 # Remove empty entries (those which are None)
patkasperbfb1f8c2019-12-05 22:03:44 +0100430 prep = {k: v for k, v in prep.items() if v is not None}
431
432 # Remove entries that explicitely need to be removed
433 if strip is not None:
434 prep = {k: v for k, v in prep.items() if k not in strip}
435
436 return json.dumps(prep)
arithmetic1728772dac62020-03-27 14:34:13 -0700437
438
Bu Sun Kim41599ae2020-09-02 12:55:42 -0600439class UserAccessTokenCredentials(credentials.CredentialsWithQuotaProject):
arithmetic1728772dac62020-03-27 14:34:13 -0700440 """Access token credentials for user account.
441
442 Obtain the access token for a given user account or the current active
443 user account with the ``gcloud auth print-access-token`` command.
444
445 Args:
446 account (Optional[str]): Account to get the access token for. If not
447 specified, the current active account will be used.
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700448 quota_project_id (Optional[str]): The project ID used for quota
449 and billing.
arithmetic1728772dac62020-03-27 14:34:13 -0700450 """
451
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700452 def __init__(self, account=None, quota_project_id=None):
arithmetic1728772dac62020-03-27 14:34:13 -0700453 super(UserAccessTokenCredentials, self).__init__()
454 self._account = account
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700455 self._quota_project_id = quota_project_id
arithmetic1728772dac62020-03-27 14:34:13 -0700456
457 def with_account(self, account):
458 """Create a new instance with the given account.
459
460 Args:
461 account (str): Account to get the access token for.
462
463 Returns:
464 google.oauth2.credentials.UserAccessTokenCredentials: The created
465 credentials with the given account.
466 """
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700467 return self.__class__(account=account, quota_project_id=self._quota_project_id)
468
Bu Sun Kim41599ae2020-09-02 12:55:42 -0600469 @_helpers.copy_docstring(credentials.CredentialsWithQuotaProject)
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700470 def with_quota_project(self, quota_project_id):
471 return self.__class__(account=self._account, quota_project_id=quota_project_id)
arithmetic1728772dac62020-03-27 14:34:13 -0700472
473 def refresh(self, request):
474 """Refreshes the access token.
475
476 Args:
477 request (google.auth.transport.Request): This argument is required
478 by the base class interface but not used in this implementation,
479 so just set it to `None`.
480
481 Raises:
482 google.auth.exceptions.UserAccessTokenError: If the access token
483 refresh failed.
484 """
485 self.token = _cloud_sdk.get_auth_access_token(self._account)
486
487 @_helpers.copy_docstring(credentials.Credentials)
488 def before_request(self, request, method, url, headers):
489 self.refresh(request)
490 self.apply(headers)