blob: 1ccfa19c0377c2235f79d9b29f58a2fcb8fc62b8 [file] [log] [blame]
C.J. Collier37141e42020-02-13 13:49:49 -08001# Copyright 2016 Google LLC
Jon Wayne Parrottab9eba32016-10-17 10:49:57 -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"""Service Accounts: JSON Web Token (JWT) Profile for OAuth 2.0
16
17This module implements the JWT Profile for OAuth 2.0 Authorization Grants
18as defined by `RFC 7523`_ with particular support for how this RFC is
19implemented in Google's infrastructure. Google refers to these credentials
20as *Service Accounts*.
21
22Service accounts are used for server-to-server communication, such as
23interactions between a web application server and a Google service. The
24service account belongs to your application instead of to an individual end
25user. In contrast to other OAuth 2.0 profiles, no users are involved and your
26application "acts" as the service account.
27
28Typically an application uses a service account when the application uses
29Google APIs to work with its own data rather than a user's data. For example,
30an application that uses Google Cloud Datastore for data persistence would use
31a service account to authenticate its calls to the Google Cloud Datastore API.
32However, an application that needs to access a user's Drive documents would
33use the normal OAuth 2.0 profile.
34
35Additionally, Google Apps domain administrators can grant service accounts
36`domain-wide delegation`_ authority to access user data on behalf of users in
37the domain.
38
39This profile uses a JWT to acquire an OAuth 2.0 access token. The JWT is used
40in place of the usual authorization token returned during the standard
41OAuth 2.0 Authorization Code grant. The JWT is only used for this purpose, as
42the acquired access token is used as the bearer token when making requests
43using these credentials.
44
45This profile differs from normal OAuth 2.0 profile because no user consent
46step is required. The use of the private key allows this profile to assert
47identity directly.
48
49This profile also differs from the :mod:`google.auth.jwt` authentication
50because the JWT credentials use the JWT directly as the bearer token. This
51profile instead only uses the JWT to obtain an OAuth 2.0 access token. The
52obtained OAuth 2.0 access token is used as the bearer token.
53
54Domain-wide delegation
55----------------------
56
57Domain-wide delegation allows a service account to access user data on
58behalf of any user in a Google Apps domain without consent from the user.
59For example, an application that uses the Google Calendar API to add events to
60the calendars of all users in a Google Apps domain would use a service account
61to access the Google Calendar API on behalf of users.
62
63The Google Apps administrator must explicitly authorize the service account to
64do this. This authorization step is referred to as "delegating domain-wide
65authority" to a service account.
66
67You can use domain-wise delegation by creating a set of credentials with a
68specific subject using :meth:`~Credentials.with_subject`.
69
70.. _RFC 7523: https://tools.ietf.org/html/rfc7523
71"""
72
Jon Wayne Parrott75c78b22017-03-23 13:14:53 -070073import copy
Jon Wayne Parrottab9eba32016-10-17 10:49:57 -070074import datetime
Jon Wayne Parrottab9eba32016-10-17 10:49:57 -070075
76from google.auth import _helpers
Jon Wayne Parrott807032c2016-10-18 09:38:26 -070077from google.auth import _service_account_info
Jon Wayne Parrottab9eba32016-10-17 10:49:57 -070078from google.auth import credentials
Jon Wayne Parrottab9eba32016-10-17 10:49:57 -070079from google.auth import jwt
80from google.oauth2 import _client
81
Albert-Jan Nijburgb7b48f12017-11-08 20:15:18 +000082_DEFAULT_TOKEN_LIFETIME_SECS = 3600 # 1 hour in seconds
Jon Wayne Parrottab9eba32016-10-17 10:49:57 -070083
84
Bu Sun Kim41599ae2020-09-02 12:55:42 -060085class Credentials(
86 credentials.Signing, credentials.Scoped, credentials.CredentialsWithQuotaProject
87):
Jon Wayne Parrottab9eba32016-10-17 10:49:57 -070088 """Service account credentials
89
90 Usually, you'll create these credentials with one of the helper
91 constructors. To create credentials using a Google service account
92 private key JSON file::
93
94 credentials = service_account.Credentials.from_service_account_file(
95 'service-account.json')
96
97 Or if you already have the service account file loaded::
98
99 service_account_info = json.load(open('service_account.json'))
100 credentials = service_account.Credentials.from_service_account_info(
101 service_account_info)
102
103 Both helper methods pass on arguments to the constructor, so you can
104 specify additional scopes and a subject if necessary::
105
106 credentials = service_account.Credentials.from_service_account_file(
107 'service-account.json',
108 scopes=['email'],
109 subject='user@example.com')
110
111 The credentials are considered immutable. If you want to modify the scopes
112 or the subject used for delegation, use :meth:`with_scopes` or
113 :meth:`with_subject`::
114
115 scoped_credentials = credentials.with_scopes(['email'])
116 delegated_credentials = credentials.with_subject(subject)
Bu Sun Kimb12488c2020-06-10 13:44:07 -0700117
118 To add a quota project, use :meth:`with_quota_project`::
119
120 credentials = credentials.with_quota_project('myproject-123')
Jon Wayne Parrottab9eba32016-10-17 10:49:57 -0700121 """
122
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700123 def __init__(
124 self,
125 signer,
126 service_account_email,
127 token_uri,
128 scopes=None,
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700129 default_scopes=None,
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700130 subject=None,
131 project_id=None,
Bu Sun Kimb12488c2020-06-10 13:44:07 -0700132 quota_project_id=None,
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700133 additional_claims=None,
134 ):
Jon Wayne Parrottab9eba32016-10-17 10:49:57 -0700135 """
136 Args:
137 signer (google.auth.crypt.Signer): The signer used to sign JWTs.
138 service_account_email (str): The service account's email.
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700139 scopes (Sequence[str]): User-defined scopes to request during the
140 authorization grant.
141 default_scopes (Sequence[str]): Default scopes passed by a
142 Google client library. Use 'scopes' for user-defined scopes.
Jon Wayne Parrottab9eba32016-10-17 10:49:57 -0700143 token_uri (str): The OAuth 2.0 Token URI.
144 subject (str): For domain-wide delegation, the email address of the
145 user to for which to request delegated access.
Hiranya Jayathilaka6a3f0ec2017-08-10 09:11:02 -0700146 project_id (str): Project ID associated with the service account
147 credential.
Bu Sun Kimb12488c2020-06-10 13:44:07 -0700148 quota_project_id (Optional[str]): The project ID used for quota and
149 billing.
Jon Wayne Parrottab9eba32016-10-17 10:49:57 -0700150 additional_claims (Mapping[str, str]): Any additional claims for
151 the JWT assertion used in the authorization grant.
152
153 .. note:: Typically one of the helper constructors
154 :meth:`from_service_account_file` or
155 :meth:`from_service_account_info` are used instead of calling the
156 constructor directly.
157 """
158 super(Credentials, self).__init__()
159
160 self._scopes = scopes
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700161 self._default_scopes = default_scopes
Jon Wayne Parrottab9eba32016-10-17 10:49:57 -0700162 self._signer = signer
163 self._service_account_email = service_account_email
164 self._subject = subject
Hiranya Jayathilaka6a3f0ec2017-08-10 09:11:02 -0700165 self._project_id = project_id
Bu Sun Kimb12488c2020-06-10 13:44:07 -0700166 self._quota_project_id = quota_project_id
Jon Wayne Parrottab9eba32016-10-17 10:49:57 -0700167 self._token_uri = token_uri
168
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700169 self._jwt_credentials = None
170
Jon Wayne Parrottab9eba32016-10-17 10:49:57 -0700171 if additional_claims is not None:
172 self._additional_claims = additional_claims
173 else:
174 self._additional_claims = {}
175
176 @classmethod
Jon Wayne Parrott807032c2016-10-18 09:38:26 -0700177 def _from_signer_and_info(cls, signer, info, **kwargs):
178 """Creates a Credentials instance from a signer and service account
179 info.
180
181 Args:
182 signer (google.auth.crypt.Signer): The signer used to sign JWTs.
183 info (Mapping[str, str]): The service account info.
184 kwargs: Additional arguments to pass to the constructor.
185
186 Returns:
187 google.auth.jwt.Credentials: The constructed credentials.
188
189 Raises:
190 ValueError: If the info is not in the expected format.
191 """
192 return cls(
193 signer,
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700194 service_account_email=info["client_email"],
195 token_uri=info["token_uri"],
196 project_id=info.get("project_id"),
197 **kwargs
198 )
Jon Wayne Parrott807032c2016-10-18 09:38:26 -0700199
200 @classmethod
Jon Wayne Parrottab9eba32016-10-17 10:49:57 -0700201 def from_service_account_info(cls, info, **kwargs):
202 """Creates a Credentials instance from parsed service account info.
203
204 Args:
205 info (Mapping[str, str]): The service account info in Google
206 format.
207 kwargs: Additional arguments to pass to the constructor.
208
209 Returns:
210 google.auth.service_account.Credentials: The constructed
211 credentials.
212
213 Raises:
214 ValueError: If the info is not in the expected format.
215 """
Jon Wayne Parrott807032c2016-10-18 09:38:26 -0700216 signer = _service_account_info.from_dict(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700217 info, require=["client_email", "token_uri"]
218 )
Jon Wayne Parrott807032c2016-10-18 09:38:26 -0700219 return cls._from_signer_and_info(signer, info, **kwargs)
Jon Wayne Parrottab9eba32016-10-17 10:49:57 -0700220
221 @classmethod
222 def from_service_account_file(cls, filename, **kwargs):
223 """Creates a Credentials instance from a service account json file.
224
225 Args:
226 filename (str): The path to the service account json file.
227 kwargs: Additional arguments to pass to the constructor.
228
229 Returns:
230 google.auth.service_account.Credentials: The constructed
231 credentials.
232 """
Jon Wayne Parrott807032c2016-10-18 09:38:26 -0700233 info, signer = _service_account_info.from_filename(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700234 filename, require=["client_email", "token_uri"]
235 )
Jon Wayne Parrott807032c2016-10-18 09:38:26 -0700236 return cls._from_signer_and_info(signer, info, **kwargs)
Jon Wayne Parrottab9eba32016-10-17 10:49:57 -0700237
Jon Wayne Parrottab9eba32016-10-17 10:49:57 -0700238 @property
Jon Wayne Parrott61ffb052016-11-08 09:30:30 -0800239 def service_account_email(self):
240 """The service account email."""
241 return self._service_account_email
242
243 @property
Hiranya Jayathilaka6a3f0ec2017-08-10 09:11:02 -0700244 def project_id(self):
245 """Project ID associated with this credential."""
246 return self._project_id
247
248 @property
Jon Wayne Parrottab9eba32016-10-17 10:49:57 -0700249 def requires_scopes(self):
250 """Checks if the credentials requires scopes.
251
252 Returns:
253 bool: True if there are no scopes set otherwise False.
254 """
255 return True if not self._scopes else False
256
257 @_helpers.copy_docstring(credentials.Scoped)
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700258 def with_scopes(self, scopes, default_scopes=None):
Christophe Tatonb649b432018-02-08 14:12:23 -0800259 return self.__class__(
Jon Wayne Parrottab9eba32016-10-17 10:49:57 -0700260 self._signer,
261 service_account_email=self._service_account_email,
262 scopes=scopes,
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700263 default_scopes=default_scopes,
Jon Wayne Parrottab9eba32016-10-17 10:49:57 -0700264 token_uri=self._token_uri,
265 subject=self._subject,
Hiranya Jayathilaka6a3f0ec2017-08-10 09:11:02 -0700266 project_id=self._project_id,
Bu Sun Kimb12488c2020-06-10 13:44:07 -0700267 quota_project_id=self._quota_project_id,
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700268 additional_claims=self._additional_claims.copy(),
269 )
Jon Wayne Parrottab9eba32016-10-17 10:49:57 -0700270
271 def with_subject(self, subject):
272 """Create a copy of these credentials with the specified subject.
273
274 Args:
275 subject (str): The subject claim.
276
277 Returns:
278 google.auth.service_account.Credentials: A new credentials
279 instance.
280 """
Christophe Tatonb649b432018-02-08 14:12:23 -0800281 return self.__class__(
Jon Wayne Parrottab9eba32016-10-17 10:49:57 -0700282 self._signer,
283 service_account_email=self._service_account_email,
284 scopes=self._scopes,
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700285 default_scopes=self._default_scopes,
Jon Wayne Parrottab9eba32016-10-17 10:49:57 -0700286 token_uri=self._token_uri,
287 subject=subject,
Hiranya Jayathilaka6a3f0ec2017-08-10 09:11:02 -0700288 project_id=self._project_id,
Bu Sun Kimb12488c2020-06-10 13:44:07 -0700289 quota_project_id=self._quota_project_id,
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700290 additional_claims=self._additional_claims.copy(),
291 )
Jon Wayne Parrottab9eba32016-10-17 10:49:57 -0700292
Jon Wayne Parrott75c78b22017-03-23 13:14:53 -0700293 def with_claims(self, additional_claims):
294 """Returns a copy of these credentials with modified claims.
295
296 Args:
297 additional_claims (Mapping[str, str]): Any additional claims for
298 the JWT payload. This will be merged with the current
299 additional claims.
300
301 Returns:
302 google.auth.service_account.Credentials: A new credentials
303 instance.
304 """
305 new_additional_claims = copy.deepcopy(self._additional_claims)
306 new_additional_claims.update(additional_claims or {})
307
Christophe Tatonb649b432018-02-08 14:12:23 -0800308 return self.__class__(
Jon Wayne Parrott75c78b22017-03-23 13:14:53 -0700309 self._signer,
310 service_account_email=self._service_account_email,
311 scopes=self._scopes,
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700312 default_scopes=self._default_scopes,
Jon Wayne Parrott75c78b22017-03-23 13:14:53 -0700313 token_uri=self._token_uri,
314 subject=self._subject,
Hiranya Jayathilaka6a3f0ec2017-08-10 09:11:02 -0700315 project_id=self._project_id,
Bu Sun Kimb12488c2020-06-10 13:44:07 -0700316 quota_project_id=self._quota_project_id,
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700317 additional_claims=new_additional_claims,
318 )
Jon Wayne Parrott75c78b22017-03-23 13:14:53 -0700319
Bu Sun Kim41599ae2020-09-02 12:55:42 -0600320 @_helpers.copy_docstring(credentials.CredentialsWithQuotaProject)
Bu Sun Kimb12488c2020-06-10 13:44:07 -0700321 def with_quota_project(self, quota_project_id):
Bu Sun Kimb12488c2020-06-10 13:44:07 -0700322
Bu Sun Kimb12488c2020-06-10 13:44:07 -0700323 return self.__class__(
324 self._signer,
325 service_account_email=self._service_account_email,
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700326 default_scopes=self._default_scopes,
Bu Sun Kimb12488c2020-06-10 13:44:07 -0700327 scopes=self._scopes,
328 token_uri=self._token_uri,
329 subject=self._subject,
330 project_id=self._project_id,
331 quota_project_id=quota_project_id,
332 additional_claims=self._additional_claims.copy(),
333 )
334
Jon Wayne Parrottab9eba32016-10-17 10:49:57 -0700335 def _make_authorization_grant_assertion(self):
336 """Create the OAuth 2.0 assertion.
337
338 This assertion is used during the OAuth 2.0 grant to acquire an
339 access token.
340
341 Returns:
342 bytes: The authorization grant assertion.
343 """
344 now = _helpers.utcnow()
345 lifetime = datetime.timedelta(seconds=_DEFAULT_TOKEN_LIFETIME_SECS)
346 expiry = now + lifetime
347
348 payload = {
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700349 "iat": _helpers.datetime_to_secs(now),
350 "exp": _helpers.datetime_to_secs(expiry),
Jon Wayne Parrottab9eba32016-10-17 10:49:57 -0700351 # The issuer must be the service account email.
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700352 "iss": self._service_account_email,
Jon Wayne Parrottab9eba32016-10-17 10:49:57 -0700353 # The audience must be the auth token endpoint's URI
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700354 "aud": self._token_uri,
355 "scope": _helpers.scopes_to_string(self._scopes or ()),
Jon Wayne Parrottab9eba32016-10-17 10:49:57 -0700356 }
357
358 payload.update(self._additional_claims)
359
360 # The subject can be a user email for domain-wide delegation.
361 if self._subject:
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700362 payload.setdefault("sub", self._subject)
Jon Wayne Parrottab9eba32016-10-17 10:49:57 -0700363
364 token = jwt.encode(self._signer, payload)
365
366 return token
367
368 @_helpers.copy_docstring(credentials.Credentials)
369 def refresh(self, request):
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700370 if self._jwt_credentials is not None:
371 self._jwt_credentials.refresh(request)
372 self.token = self._jwt_credentials.token
373 self.expiry = self._jwt_credentials.expiry
374 else:
375 assertion = self._make_authorization_grant_assertion()
376 access_token, expiry, _ = _client.jwt_grant(
377 request, self._token_uri, assertion
378 )
379 self.token = access_token
380 self.expiry = expiry
381
382 def _create_self_signed_jwt(self, audience):
383 """Create a self-signed JWT from the credentials if requirements are met.
384
385 Args:
386 audience (str): The service URL. ``https://[API_ENDPOINT]/``
387 """
388 # https://google.aip.dev/auth/4111
389 # If the user has not defined scopes, create a self-signed jwt
390 if not self.scopes:
391 self._jwt_credentials = jwt.Credentials.from_signing_credentials(
392 self, audience
393 )
Jon Wayne Parrottab9eba32016-10-17 10:49:57 -0700394
395 @_helpers.copy_docstring(credentials.Signing)
396 def sign_bytes(self, message):
397 return self._signer.sign(message)
Jon Wayne Parrott4c883f02016-12-02 14:26:33 -0800398
399 @property
400 @_helpers.copy_docstring(credentials.Signing)
Jon Wayne Parrottd7221672017-02-16 09:05:11 -0800401 def signer(self):
402 return self._signer
403
404 @property
405 @_helpers.copy_docstring(credentials.Signing)
Jon Wayne Parrott4c883f02016-12-02 14:26:33 -0800406 def signer_email(self):
407 return self._service_account_email
Christophe Tatonb649b432018-02-08 14:12:23 -0800408
409
Bu Sun Kim41599ae2020-09-02 12:55:42 -0600410class IDTokenCredentials(credentials.Signing, credentials.CredentialsWithQuotaProject):
Christophe Tatonb649b432018-02-08 14:12:23 -0800411 """Open ID Connect ID Token-based service account credentials.
412
413 These credentials are largely similar to :class:`.Credentials`, but instead
414 of using an OAuth 2.0 Access Token as the bearer token, they use an Open
415 ID Connect ID Token as the bearer token. These credentials are useful when
416 communicating to services that require ID Tokens and can not accept access
417 tokens.
418
419 Usually, you'll create these credentials with one of the helper
420 constructors. To create credentials using a Google service account
421 private key JSON file::
422
423 credentials = (
424 service_account.IDTokenCredentials.from_service_account_file(
425 'service-account.json'))
426
Bu Sun Kimb1a12d22021-02-26 11:50:02 -0700427
Christophe Tatonb649b432018-02-08 14:12:23 -0800428 Or if you already have the service account file loaded::
429
430 service_account_info = json.load(open('service_account.json'))
431 credentials = (
432 service_account.IDTokenCredentials.from_service_account_info(
433 service_account_info))
434
Bu Sun Kimb1a12d22021-02-26 11:50:02 -0700435
Christophe Tatonb649b432018-02-08 14:12:23 -0800436 Both helper methods pass on arguments to the constructor, so you can
437 specify additional scopes and a subject if necessary::
438
439 credentials = (
440 service_account.IDTokenCredentials.from_service_account_file(
441 'service-account.json',
442 scopes=['email'],
443 subject='user@example.com'))
Bu Sun Kimb1a12d22021-02-26 11:50:02 -0700444
445
Christophe Tatonb649b432018-02-08 14:12:23 -0800446 The credentials are considered immutable. If you want to modify the scopes
447 or the subject used for delegation, use :meth:`with_scopes` or
448 :meth:`with_subject`::
449
450 scoped_credentials = credentials.with_scopes(['email'])
451 delegated_credentials = credentials.with_subject(subject)
452
453 """
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700454
455 def __init__(
456 self,
457 signer,
458 service_account_email,
459 token_uri,
460 target_audience,
461 additional_claims=None,
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700462 quota_project_id=None,
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700463 ):
Christophe Tatonb649b432018-02-08 14:12:23 -0800464 """
465 Args:
466 signer (google.auth.crypt.Signer): The signer used to sign JWTs.
467 service_account_email (str): The service account's email.
468 token_uri (str): The OAuth 2.0 Token URI.
469 target_audience (str): The intended audience for these credentials,
470 used when requesting the ID Token. The ID Token's ``aud`` claim
471 will be set to this string.
472 additional_claims (Mapping[str, str]): Any additional claims for
473 the JWT assertion used in the authorization grant.
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700474 quota_project_id (Optional[str]): The project ID used for quota and billing.
Christophe Tatonb649b432018-02-08 14:12:23 -0800475 .. note:: Typically one of the helper constructors
476 :meth:`from_service_account_file` or
477 :meth:`from_service_account_info` are used instead of calling the
478 constructor directly.
479 """
480 super(IDTokenCredentials, self).__init__()
481 self._signer = signer
482 self._service_account_email = service_account_email
483 self._token_uri = token_uri
484 self._target_audience = target_audience
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700485 self._quota_project_id = quota_project_id
Christophe Tatonb649b432018-02-08 14:12:23 -0800486
487 if additional_claims is not None:
488 self._additional_claims = additional_claims
489 else:
490 self._additional_claims = {}
491
492 @classmethod
493 def _from_signer_and_info(cls, signer, info, **kwargs):
494 """Creates a credentials instance from a signer and service account
495 info.
496
497 Args:
498 signer (google.auth.crypt.Signer): The signer used to sign JWTs.
499 info (Mapping[str, str]): The service account info.
500 kwargs: Additional arguments to pass to the constructor.
501
502 Returns:
503 google.auth.jwt.IDTokenCredentials: The constructed credentials.
504
505 Raises:
506 ValueError: If the info is not in the expected format.
507 """
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700508 kwargs.setdefault("service_account_email", info["client_email"])
509 kwargs.setdefault("token_uri", info["token_uri"])
Christophe Tatonb649b432018-02-08 14:12:23 -0800510 return cls(signer, **kwargs)
511
512 @classmethod
513 def from_service_account_info(cls, info, **kwargs):
514 """Creates a credentials instance from parsed service account info.
515
516 Args:
517 info (Mapping[str, str]): The service account info in Google
518 format.
519 kwargs: Additional arguments to pass to the constructor.
520
521 Returns:
522 google.auth.service_account.IDTokenCredentials: The constructed
523 credentials.
524
525 Raises:
526 ValueError: If the info is not in the expected format.
527 """
528 signer = _service_account_info.from_dict(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700529 info, require=["client_email", "token_uri"]
530 )
Christophe Tatonb649b432018-02-08 14:12:23 -0800531 return cls._from_signer_and_info(signer, info, **kwargs)
532
533 @classmethod
534 def from_service_account_file(cls, filename, **kwargs):
535 """Creates a credentials instance from a service account json file.
536
537 Args:
538 filename (str): The path to the service account json file.
539 kwargs: Additional arguments to pass to the constructor.
540
541 Returns:
542 google.auth.service_account.IDTokenCredentials: The constructed
543 credentials.
544 """
545 info, signer = _service_account_info.from_filename(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700546 filename, require=["client_email", "token_uri"]
547 )
Christophe Tatonb649b432018-02-08 14:12:23 -0800548 return cls._from_signer_and_info(signer, info, **kwargs)
549
550 def with_target_audience(self, target_audience):
551 """Create a copy of these credentials with the specified target
552 audience.
553
554 Args:
555 target_audience (str): The intended audience for these credentials,
556 used when requesting the ID Token.
557
558 Returns:
559 google.auth.service_account.IDTokenCredentials: A new credentials
560 instance.
561 """
562 return self.__class__(
563 self._signer,
564 service_account_email=self._service_account_email,
565 token_uri=self._token_uri,
566 target_audience=target_audience,
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700567 additional_claims=self._additional_claims.copy(),
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700568 quota_project_id=self.quota_project_id,
569 )
570
Bu Sun Kim41599ae2020-09-02 12:55:42 -0600571 @_helpers.copy_docstring(credentials.CredentialsWithQuotaProject)
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700572 def with_quota_project(self, quota_project_id):
573 return self.__class__(
574 self._signer,
575 service_account_email=self._service_account_email,
576 token_uri=self._token_uri,
577 target_audience=self._target_audience,
578 additional_claims=self._additional_claims.copy(),
579 quota_project_id=quota_project_id,
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700580 )
Christophe Tatonb649b432018-02-08 14:12:23 -0800581
582 def _make_authorization_grant_assertion(self):
583 """Create the OAuth 2.0 assertion.
584
585 This assertion is used during the OAuth 2.0 grant to acquire an
586 ID token.
587
588 Returns:
589 bytes: The authorization grant assertion.
590 """
591 now = _helpers.utcnow()
592 lifetime = datetime.timedelta(seconds=_DEFAULT_TOKEN_LIFETIME_SECS)
593 expiry = now + lifetime
594
595 payload = {
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700596 "iat": _helpers.datetime_to_secs(now),
597 "exp": _helpers.datetime_to_secs(expiry),
Christophe Tatonb649b432018-02-08 14:12:23 -0800598 # The issuer must be the service account email.
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700599 "iss": self.service_account_email,
Christophe Tatonb649b432018-02-08 14:12:23 -0800600 # The audience must be the auth token endpoint's URI
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700601 "aud": self._token_uri,
Christophe Tatonb649b432018-02-08 14:12:23 -0800602 # The target audience specifies which service the ID token is
603 # intended for.
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700604 "target_audience": self._target_audience,
Christophe Tatonb649b432018-02-08 14:12:23 -0800605 }
606
607 payload.update(self._additional_claims)
608
609 token = jwt.encode(self._signer, payload)
610
611 return token
612
613 @_helpers.copy_docstring(credentials.Credentials)
614 def refresh(self, request):
615 assertion = self._make_authorization_grant_assertion()
616 access_token, expiry, _ = _client.id_token_jwt_grant(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700617 request, self._token_uri, assertion
618 )
Christophe Tatonb649b432018-02-08 14:12:23 -0800619 self.token = access_token
620 self.expiry = expiry
621
622 @property
623 def service_account_email(self):
624 """The service account email."""
625 return self._service_account_email
626
627 @_helpers.copy_docstring(credentials.Signing)
628 def sign_bytes(self, message):
629 return self._signer.sign(message)
630
631 @property
632 @_helpers.copy_docstring(credentials.Signing)
633 def signer(self):
634 return self._signer
635
636 @property
637 @_helpers.copy_docstring(credentials.Signing)
638 def signer_email(self):
639 return self._service_account_email