blob: ed91011428b080b90cd63b4cd25cb3af6608b5af [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
427 Or if you already have the service account file loaded::
428
429 service_account_info = json.load(open('service_account.json'))
430 credentials = (
431 service_account.IDTokenCredentials.from_service_account_info(
432 service_account_info))
433
434 Both helper methods pass on arguments to the constructor, so you can
435 specify additional scopes and a subject if necessary::
436
437 credentials = (
438 service_account.IDTokenCredentials.from_service_account_file(
439 'service-account.json',
440 scopes=['email'],
441 subject='user@example.com'))
442`
443 The credentials are considered immutable. If you want to modify the scopes
444 or the subject used for delegation, use :meth:`with_scopes` or
445 :meth:`with_subject`::
446
447 scoped_credentials = credentials.with_scopes(['email'])
448 delegated_credentials = credentials.with_subject(subject)
449
450 """
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700451
452 def __init__(
453 self,
454 signer,
455 service_account_email,
456 token_uri,
457 target_audience,
458 additional_claims=None,
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700459 quota_project_id=None,
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700460 ):
Christophe Tatonb649b432018-02-08 14:12:23 -0800461 """
462 Args:
463 signer (google.auth.crypt.Signer): The signer used to sign JWTs.
464 service_account_email (str): The service account's email.
465 token_uri (str): The OAuth 2.0 Token URI.
466 target_audience (str): The intended audience for these credentials,
467 used when requesting the ID Token. The ID Token's ``aud`` claim
468 will be set to this string.
469 additional_claims (Mapping[str, str]): Any additional claims for
470 the JWT assertion used in the authorization grant.
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700471 quota_project_id (Optional[str]): The project ID used for quota and billing.
Christophe Tatonb649b432018-02-08 14:12:23 -0800472 .. note:: Typically one of the helper constructors
473 :meth:`from_service_account_file` or
474 :meth:`from_service_account_info` are used instead of calling the
475 constructor directly.
476 """
477 super(IDTokenCredentials, self).__init__()
478 self._signer = signer
479 self._service_account_email = service_account_email
480 self._token_uri = token_uri
481 self._target_audience = target_audience
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700482 self._quota_project_id = quota_project_id
Christophe Tatonb649b432018-02-08 14:12:23 -0800483
484 if additional_claims is not None:
485 self._additional_claims = additional_claims
486 else:
487 self._additional_claims = {}
488
489 @classmethod
490 def _from_signer_and_info(cls, signer, info, **kwargs):
491 """Creates a credentials instance from a signer and service account
492 info.
493
494 Args:
495 signer (google.auth.crypt.Signer): The signer used to sign JWTs.
496 info (Mapping[str, str]): The service account info.
497 kwargs: Additional arguments to pass to the constructor.
498
499 Returns:
500 google.auth.jwt.IDTokenCredentials: The constructed credentials.
501
502 Raises:
503 ValueError: If the info is not in the expected format.
504 """
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700505 kwargs.setdefault("service_account_email", info["client_email"])
506 kwargs.setdefault("token_uri", info["token_uri"])
Christophe Tatonb649b432018-02-08 14:12:23 -0800507 return cls(signer, **kwargs)
508
509 @classmethod
510 def from_service_account_info(cls, info, **kwargs):
511 """Creates a credentials instance from parsed service account info.
512
513 Args:
514 info (Mapping[str, str]): The service account info in Google
515 format.
516 kwargs: Additional arguments to pass to the constructor.
517
518 Returns:
519 google.auth.service_account.IDTokenCredentials: The constructed
520 credentials.
521
522 Raises:
523 ValueError: If the info is not in the expected format.
524 """
525 signer = _service_account_info.from_dict(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700526 info, require=["client_email", "token_uri"]
527 )
Christophe Tatonb649b432018-02-08 14:12:23 -0800528 return cls._from_signer_and_info(signer, info, **kwargs)
529
530 @classmethod
531 def from_service_account_file(cls, filename, **kwargs):
532 """Creates a credentials instance from a service account json file.
533
534 Args:
535 filename (str): The path to the service account json file.
536 kwargs: Additional arguments to pass to the constructor.
537
538 Returns:
539 google.auth.service_account.IDTokenCredentials: The constructed
540 credentials.
541 """
542 info, signer = _service_account_info.from_filename(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700543 filename, require=["client_email", "token_uri"]
544 )
Christophe Tatonb649b432018-02-08 14:12:23 -0800545 return cls._from_signer_and_info(signer, info, **kwargs)
546
547 def with_target_audience(self, target_audience):
548 """Create a copy of these credentials with the specified target
549 audience.
550
551 Args:
552 target_audience (str): The intended audience for these credentials,
553 used when requesting the ID Token.
554
555 Returns:
556 google.auth.service_account.IDTokenCredentials: A new credentials
557 instance.
558 """
559 return self.__class__(
560 self._signer,
561 service_account_email=self._service_account_email,
562 token_uri=self._token_uri,
563 target_audience=target_audience,
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700564 additional_claims=self._additional_claims.copy(),
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700565 quota_project_id=self.quota_project_id,
566 )
567
Bu Sun Kim41599ae2020-09-02 12:55:42 -0600568 @_helpers.copy_docstring(credentials.CredentialsWithQuotaProject)
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700569 def with_quota_project(self, quota_project_id):
570 return self.__class__(
571 self._signer,
572 service_account_email=self._service_account_email,
573 token_uri=self._token_uri,
574 target_audience=self._target_audience,
575 additional_claims=self._additional_claims.copy(),
576 quota_project_id=quota_project_id,
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700577 )
Christophe Tatonb649b432018-02-08 14:12:23 -0800578
579 def _make_authorization_grant_assertion(self):
580 """Create the OAuth 2.0 assertion.
581
582 This assertion is used during the OAuth 2.0 grant to acquire an
583 ID token.
584
585 Returns:
586 bytes: The authorization grant assertion.
587 """
588 now = _helpers.utcnow()
589 lifetime = datetime.timedelta(seconds=_DEFAULT_TOKEN_LIFETIME_SECS)
590 expiry = now + lifetime
591
592 payload = {
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700593 "iat": _helpers.datetime_to_secs(now),
594 "exp": _helpers.datetime_to_secs(expiry),
Christophe Tatonb649b432018-02-08 14:12:23 -0800595 # The issuer must be the service account email.
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700596 "iss": self.service_account_email,
Christophe Tatonb649b432018-02-08 14:12:23 -0800597 # The audience must be the auth token endpoint's URI
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700598 "aud": self._token_uri,
Christophe Tatonb649b432018-02-08 14:12:23 -0800599 # The target audience specifies which service the ID token is
600 # intended for.
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700601 "target_audience": self._target_audience,
Christophe Tatonb649b432018-02-08 14:12:23 -0800602 }
603
604 payload.update(self._additional_claims)
605
606 token = jwt.encode(self._signer, payload)
607
608 return token
609
610 @_helpers.copy_docstring(credentials.Credentials)
611 def refresh(self, request):
612 assertion = self._make_authorization_grant_assertion()
613 access_token, expiry, _ = _client.id_token_jwt_grant(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700614 request, self._token_uri, assertion
615 )
Christophe Tatonb649b432018-02-08 14:12:23 -0800616 self.token = access_token
617 self.expiry = expiry
618
619 @property
620 def service_account_email(self):
621 """The service account email."""
622 return self._service_account_email
623
624 @_helpers.copy_docstring(credentials.Signing)
625 def sign_bytes(self, message):
626 return self._signer.sign(message)
627
628 @property
629 @_helpers.copy_docstring(credentials.Signing)
630 def signer(self):
631 return self._signer
632
633 @property
634 @_helpers.copy_docstring(credentials.Signing)
635 def signer_email(self):
636 return self._service_account_email