1# Copyright 2016 Google LLC
2#
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"""JSON Web Tokens
16
17Provides support for creating (encoding) and verifying (decoding) JWTs,
18especially JWTs generated and consumed by Google infrastructure.
19
20See `rfc7519`_ for more details on JWTs.
21
22To encode a JWT use :func:`encode`::
23
24    from google.auth import crypt
25    from google.auth import jwt
26
27    signer = crypt.Signer(private_key)
28    payload = {'some': 'payload'}
29    encoded = jwt.encode(signer, payload)
30
31To decode a JWT and verify claims use :func:`decode`::
32
33    claims = jwt.decode(encoded, certs=public_certs)
34
35You can also skip verification::
36
37    claims = jwt.decode(encoded, verify=False)
38
39.. _rfc7519: https://tools.ietf.org/html/rfc7519
40
41"""
42
43try:
44    from collections.abc import Mapping
45# Python 2.7 compatibility
46except ImportError:  # pragma: NO COVER
47    from collections import Mapping
48import copy
49import datetime
50import json
51
52import cachetools
53import six
54from six.moves import urllib
55
56from google.auth import _helpers
57from google.auth import _service_account_info
58from google.auth import crypt
59from google.auth import exceptions
60import google.auth.credentials
61
62try:
63    from google.auth.crypt import es256
64except ImportError:  # pragma: NO COVER
65    es256 = None
66
67_DEFAULT_TOKEN_LIFETIME_SECS = 3600  # 1 hour in seconds
68_DEFAULT_MAX_CACHE_SIZE = 10
69_ALGORITHM_TO_VERIFIER_CLASS = {"RS256": crypt.RSAVerifier}
70_CRYPTOGRAPHY_BASED_ALGORITHMS = frozenset(["ES256"])
71
72if es256 is not None:  # pragma: NO COVER
73    _ALGORITHM_TO_VERIFIER_CLASS["ES256"] = es256.ES256Verifier
74
75
76def encode(signer, payload, header=None, key_id=None):
77    """Make a signed JWT.
78
79    Args:
80        signer (google.auth.crypt.Signer): The signer used to sign the JWT.
81        payload (Mapping[str, str]): The JWT payload.
82        header (Mapping[str, str]): Additional JWT header payload.
83        key_id (str): The key id to add to the JWT header. If the
84            signer has a key id it will be used as the default. If this is
85            specified it will override the signer's key id.
86
87    Returns:
88        bytes: The encoded JWT.
89    """
90    if header is None:
91        header = {}
92
93    if key_id is None:
94        key_id = signer.key_id
95
96    header.update({"typ": "JWT"})
97
98    if "alg" not in header:
99        if es256 is not None and isinstance(signer, es256.ES256Signer):
100            header.update({"alg": "ES256"})
101        else:
102            header.update({"alg": "RS256"})
103
104    if key_id is not None:
105        header["kid"] = key_id
106
107    segments = [
108        _helpers.unpadded_urlsafe_b64encode(json.dumps(header).encode("utf-8")),
109        _helpers.unpadded_urlsafe_b64encode(json.dumps(payload).encode("utf-8")),
110    ]
111
112    signing_input = b".".join(segments)
113    signature = signer.sign(signing_input)
114    segments.append(_helpers.unpadded_urlsafe_b64encode(signature))
115
116    return b".".join(segments)
117
118
119def _decode_jwt_segment(encoded_section):
120    """Decodes a single JWT segment."""
121    section_bytes = _helpers.padded_urlsafe_b64decode(encoded_section)
122    try:
123        return json.loads(section_bytes.decode("utf-8"))
124    except ValueError as caught_exc:
125        new_exc = ValueError("Can't parse segment: {0}".format(section_bytes))
126        six.raise_from(new_exc, caught_exc)
127
128
129def _unverified_decode(token):
130    """Decodes a token and does no verification.
131
132    Args:
133        token (Union[str, bytes]): The encoded JWT.
134
135    Returns:
136        Tuple[str, str, str, str]: header, payload, signed_section, and
137            signature.
138
139    Raises:
140        ValueError: if there are an incorrect amount of segments in the token.
141    """
142    token = _helpers.to_bytes(token)
143
144    if token.count(b".") != 2:
145        raise ValueError("Wrong number of segments in token: {0}".format(token))
146
147    encoded_header, encoded_payload, signature = token.split(b".")
148    signed_section = encoded_header + b"." + encoded_payload
149    signature = _helpers.padded_urlsafe_b64decode(signature)
150
151    # Parse segments
152    header = _decode_jwt_segment(encoded_header)
153    payload = _decode_jwt_segment(encoded_payload)
154
155    return header, payload, signed_section, signature
156
157
158def decode_header(token):
159    """Return the decoded header of a token.
160
161    No verification is done. This is useful to extract the key id from
162    the header in order to acquire the appropriate certificate to verify
163    the token.
164
165    Args:
166        token (Union[str, bytes]): the encoded JWT.
167
168    Returns:
169        Mapping: The decoded JWT header.
170    """
171    header, _, _, _ = _unverified_decode(token)
172    return header
173
174
175def _verify_iat_and_exp(payload, clock_skew_in_seconds=0):
176    """Verifies the ``iat`` (Issued At) and ``exp`` (Expires) claims in a token
177    payload.
178
179    Args:
180        payload (Mapping[str, str]): The JWT payload.
181        clock_skew_in_seconds (int): The clock skew used for `iat` and `exp`
182            validation.
183
184    Raises:
185        ValueError: if any checks failed.
186    """
187    now = _helpers.datetime_to_secs(_helpers.utcnow())
188
189    # Make sure the iat and exp claims are present.
190    for key in ("iat", "exp"):
191        if key not in payload:
192            raise ValueError("Token does not contain required claim {}".format(key))
193
194    # Make sure the token wasn't issued in the future.
195    iat = payload["iat"]
196    # Err on the side of accepting a token that is slightly early to account
197    # for clock skew.
198    earliest = iat - clock_skew_in_seconds
199    if now < earliest:
200        raise ValueError(
201            "Token used too early, {} < {}. Check that your computer's clock is set correctly.".format(
202                now, iat
203            )
204        )
205
206    # Make sure the token wasn't issued in the past.
207    exp = payload["exp"]
208    # Err on the side of accepting a token that is slightly out of date
209    # to account for clow skew.
210    latest = exp + clock_skew_in_seconds
211    if latest < now:
212        raise ValueError("Token expired, {} < {}".format(latest, now))
213
214
215def decode(token, certs=None, verify=True, audience=None, clock_skew_in_seconds=0):
216    """Decode and verify a JWT.
217
218    Args:
219        token (str): The encoded JWT.
220        certs (Union[str, bytes, Mapping[str, Union[str, bytes]]]): The
221            certificate used to validate the JWT signature. If bytes or string,
222            it must the the public key certificate in PEM format. If a mapping,
223            it must be a mapping of key IDs to public key certificates in PEM
224            format. The mapping must contain the same key ID that's specified
225            in the token's header.
226        verify (bool): Whether to perform signature and claim validation.
227            Verification is done by default.
228        audience (str or list): The audience claim, 'aud', that this JWT should
229            contain. Or a list of audience claims. If None then the JWT's 'aud'
230            parameter is not verified.
231        clock_skew_in_seconds (int): The clock skew used for `iat` and `exp`
232            validation.
233
234    Returns:
235        Mapping[str, str]: The deserialized JSON payload in the JWT.
236
237    Raises:
238        ValueError: if any verification checks failed.
239    """
240    header, payload, signed_section, signature = _unverified_decode(token)
241
242    if not verify:
243        return payload
244
245    # Pluck the key id and algorithm from the header and make sure we have
246    # a verifier that can support it.
247    key_alg = header.get("alg")
248    key_id = header.get("kid")
249
250    try:
251        verifier_cls = _ALGORITHM_TO_VERIFIER_CLASS[key_alg]
252    except KeyError as exc:
253        if key_alg in _CRYPTOGRAPHY_BASED_ALGORITHMS:
254            six.raise_from(
255                ValueError(
256                    "The key algorithm {} requires the cryptography package "
257                    "to be installed.".format(key_alg)
258                ),
259                exc,
260            )
261        else:
262            six.raise_from(
263                ValueError("Unsupported signature algorithm {}".format(key_alg)), exc
264            )
265
266    # If certs is specified as a dictionary of key IDs to certificates, then
267    # use the certificate identified by the key ID in the token header.
268    if isinstance(certs, Mapping):
269        if key_id:
270            if key_id not in certs:
271                raise ValueError("Certificate for key id {} not found.".format(key_id))
272            certs_to_check = [certs[key_id]]
273        # If there's no key id in the header, check against all of the certs.
274        else:
275            certs_to_check = certs.values()
276    else:
277        certs_to_check = certs
278
279    # Verify that the signature matches the message.
280    if not crypt.verify_signature(
281        signed_section, signature, certs_to_check, verifier_cls
282    ):
283        raise ValueError("Could not verify token signature.")
284
285    # Verify the issued at and created times in the payload.
286    _verify_iat_and_exp(payload, clock_skew_in_seconds)
287
288    # Check audience.
289    if audience is not None:
290        claim_audience = payload.get("aud")
291        if isinstance(audience, str):
292            audience = [audience]
293        if claim_audience not in audience:
294            raise ValueError(
295                "Token has wrong audience {}, expected one of {}".format(
296                    claim_audience, audience
297                )
298            )
299
300    return payload
301
302
303class Credentials(
304    google.auth.credentials.Signing, google.auth.credentials.CredentialsWithQuotaProject
305):
306    """Credentials that use a JWT as the bearer token.
307
308    These credentials require an "audience" claim. This claim identifies the
309    intended recipient of the bearer token.
310
311    The constructor arguments determine the claims for the JWT that is
312    sent with requests. Usually, you'll construct these credentials with
313    one of the helper constructors as shown in the next section.
314
315    To create JWT credentials using a Google service account private key
316    JSON file::
317
318        audience = 'https://pubsub.googleapis.com/google.pubsub.v1.Publisher'
319        credentials = jwt.Credentials.from_service_account_file(
320            'service-account.json',
321            audience=audience)
322
323    If you already have the service account file loaded and parsed::
324
325        service_account_info = json.load(open('service_account.json'))
326        credentials = jwt.Credentials.from_service_account_info(
327            service_account_info,
328            audience=audience)
329
330    Both helper methods pass on arguments to the constructor, so you can
331    specify the JWT claims::
332
333        credentials = jwt.Credentials.from_service_account_file(
334            'service-account.json',
335            audience=audience,
336            additional_claims={'meta': 'data'})
337
338    You can also construct the credentials directly if you have a
339    :class:`~google.auth.crypt.Signer` instance::
340
341        credentials = jwt.Credentials(
342            signer,
343            issuer='your-issuer',
344            subject='your-subject',
345            audience=audience)
346
347    The claims are considered immutable. If you want to modify the claims,
348    you can easily create another instance using :meth:`with_claims`::
349
350        new_audience = (
351            'https://pubsub.googleapis.com/google.pubsub.v1.Subscriber')
352        new_credentials = credentials.with_claims(audience=new_audience)
353    """
354
355    def __init__(
356        self,
357        signer,
358        issuer,
359        subject,
360        audience,
361        additional_claims=None,
362        token_lifetime=_DEFAULT_TOKEN_LIFETIME_SECS,
363        quota_project_id=None,
364    ):
365        """
366        Args:
367            signer (google.auth.crypt.Signer): The signer used to sign JWTs.
368            issuer (str): The `iss` claim.
369            subject (str): The `sub` claim.
370            audience (str): the `aud` claim. The intended audience for the
371                credentials.
372            additional_claims (Mapping[str, str]): Any additional claims for
373                the JWT payload.
374            token_lifetime (int): The amount of time in seconds for
375                which the token is valid. Defaults to 1 hour.
376            quota_project_id (Optional[str]): The project ID used for quota
377                and billing.
378        """
379        super(Credentials, self).__init__()
380        self._signer = signer
381        self._issuer = issuer
382        self._subject = subject
383        self._audience = audience
384        self._token_lifetime = token_lifetime
385        self._quota_project_id = quota_project_id
386
387        if additional_claims is None:
388            additional_claims = {}
389
390        self._additional_claims = additional_claims
391
392    @classmethod
393    def _from_signer_and_info(cls, signer, info, **kwargs):
394        """Creates a Credentials instance from a signer and service account
395        info.
396
397        Args:
398            signer (google.auth.crypt.Signer): The signer used to sign JWTs.
399            info (Mapping[str, str]): The service account info.
400            kwargs: Additional arguments to pass to the constructor.
401
402        Returns:
403            google.auth.jwt.Credentials: The constructed credentials.
404
405        Raises:
406            ValueError: If the info is not in the expected format.
407        """
408        kwargs.setdefault("subject", info["client_email"])
409        kwargs.setdefault("issuer", info["client_email"])
410        return cls(signer, **kwargs)
411
412    @classmethod
413    def from_service_account_info(cls, info, **kwargs):
414        """Creates an Credentials instance from a dictionary.
415
416        Args:
417            info (Mapping[str, str]): The service account info in Google
418                format.
419            kwargs: Additional arguments to pass to the constructor.
420
421        Returns:
422            google.auth.jwt.Credentials: The constructed credentials.
423
424        Raises:
425            ValueError: If the info is not in the expected format.
426        """
427        signer = _service_account_info.from_dict(info, require=["client_email"])
428        return cls._from_signer_and_info(signer, info, **kwargs)
429
430    @classmethod
431    def from_service_account_file(cls, filename, **kwargs):
432        """Creates a Credentials instance from a service account .json file
433        in Google format.
434
435        Args:
436            filename (str): The path to the service account .json file.
437            kwargs: Additional arguments to pass to the constructor.
438
439        Returns:
440            google.auth.jwt.Credentials: The constructed credentials.
441        """
442        info, signer = _service_account_info.from_filename(
443            filename, require=["client_email"]
444        )
445        return cls._from_signer_and_info(signer, info, **kwargs)
446
447    @classmethod
448    def from_signing_credentials(cls, credentials, audience, **kwargs):
449        """Creates a new :class:`google.auth.jwt.Credentials` instance from an
450        existing :class:`google.auth.credentials.Signing` instance.
451
452        The new instance will use the same signer as the existing instance and
453        will use the existing instance's signer email as the issuer and
454        subject by default.
455
456        Example::
457
458            svc_creds = service_account.Credentials.from_service_account_file(
459                'service_account.json')
460            audience = (
461                'https://pubsub.googleapis.com/google.pubsub.v1.Publisher')
462            jwt_creds = jwt.Credentials.from_signing_credentials(
463                svc_creds, audience=audience)
464
465        Args:
466            credentials (google.auth.credentials.Signing): The credentials to
467                use to construct the new credentials.
468            audience (str): the `aud` claim. The intended audience for the
469                credentials.
470            kwargs: Additional arguments to pass to the constructor.
471
472        Returns:
473            google.auth.jwt.Credentials: A new Credentials instance.
474        """
475        kwargs.setdefault("issuer", credentials.signer_email)
476        kwargs.setdefault("subject", credentials.signer_email)
477        return cls(credentials.signer, audience=audience, **kwargs)
478
479    def with_claims(
480        self, issuer=None, subject=None, audience=None, additional_claims=None
481    ):
482        """Returns a copy of these credentials with modified claims.
483
484        Args:
485            issuer (str): The `iss` claim. If unspecified the current issuer
486                claim will be used.
487            subject (str): The `sub` claim. If unspecified the current subject
488                claim will be used.
489            audience (str): the `aud` claim. If unspecified the current
490                audience claim will be used.
491            additional_claims (Mapping[str, str]): Any additional claims for
492                the JWT payload. This will be merged with the current
493                additional claims.
494
495        Returns:
496            google.auth.jwt.Credentials: A new credentials instance.
497        """
498        new_additional_claims = copy.deepcopy(self._additional_claims)
499        new_additional_claims.update(additional_claims or {})
500
501        return self.__class__(
502            self._signer,
503            issuer=issuer if issuer is not None else self._issuer,
504            subject=subject if subject is not None else self._subject,
505            audience=audience if audience is not None else self._audience,
506            additional_claims=new_additional_claims,
507            quota_project_id=self._quota_project_id,
508        )
509
510    @_helpers.copy_docstring(google.auth.credentials.CredentialsWithQuotaProject)
511    def with_quota_project(self, quota_project_id):
512        return self.__class__(
513            self._signer,
514            issuer=self._issuer,
515            subject=self._subject,
516            audience=self._audience,
517            additional_claims=self._additional_claims,
518            quota_project_id=quota_project_id,
519        )
520
521    def _make_jwt(self):
522        """Make a signed JWT.
523
524        Returns:
525            Tuple[bytes, datetime]: The encoded JWT and the expiration.
526        """
527        now = _helpers.utcnow()
528        lifetime = datetime.timedelta(seconds=self._token_lifetime)
529        expiry = now + lifetime
530
531        payload = {
532            "iss": self._issuer,
533            "sub": self._subject,
534            "iat": _helpers.datetime_to_secs(now),
535            "exp": _helpers.datetime_to_secs(expiry),
536        }
537        if self._audience:
538            payload["aud"] = self._audience
539
540        payload.update(self._additional_claims)
541
542        jwt = encode(self._signer, payload)
543
544        return jwt, expiry
545
546    def refresh(self, request):
547        """Refreshes the access token.
548
549        Args:
550            request (Any): Unused.
551        """
552        # pylint: disable=unused-argument
553        # (pylint doesn't correctly recognize overridden methods.)
554        self.token, self.expiry = self._make_jwt()
555
556    @_helpers.copy_docstring(google.auth.credentials.Signing)
557    def sign_bytes(self, message):
558        return self._signer.sign(message)
559
560    @property
561    @_helpers.copy_docstring(google.auth.credentials.Signing)
562    def signer_email(self):
563        return self._issuer
564
565    @property
566    @_helpers.copy_docstring(google.auth.credentials.Signing)
567    def signer(self):
568        return self._signer
569
570
571class OnDemandCredentials(
572    google.auth.credentials.Signing, google.auth.credentials.CredentialsWithQuotaProject
573):
574    """On-demand JWT credentials.
575
576    Like :class:`Credentials`, this class uses a JWT as the bearer token for
577    authentication. However, this class does not require the audience at
578    construction time. Instead, it will generate a new token on-demand for
579    each request using the request URI as the audience. It caches tokens
580    so that multiple requests to the same URI do not incur the overhead
581    of generating a new token every time.
582
583    This behavior is especially useful for `gRPC`_ clients. A gRPC service may
584    have multiple audience and gRPC clients may not know all of the audiences
585    required for accessing a particular service. With these credentials,
586    no knowledge of the audiences is required ahead of time.
587
588    .. _grpc: http://www.grpc.io/
589    """
590
591    def __init__(
592        self,
593        signer,
594        issuer,
595        subject,
596        additional_claims=None,
597        token_lifetime=_DEFAULT_TOKEN_LIFETIME_SECS,
598        max_cache_size=_DEFAULT_MAX_CACHE_SIZE,
599        quota_project_id=None,
600    ):
601        """
602        Args:
603            signer (google.auth.crypt.Signer): The signer used to sign JWTs.
604            issuer (str): The `iss` claim.
605            subject (str): The `sub` claim.
606            additional_claims (Mapping[str, str]): Any additional claims for
607                the JWT payload.
608            token_lifetime (int): The amount of time in seconds for
609                which the token is valid. Defaults to 1 hour.
610            max_cache_size (int): The maximum number of JWT tokens to keep in
611                cache. Tokens are cached using :class:`cachetools.LRUCache`.
612            quota_project_id (Optional[str]): The project ID used for quota
613                and billing.
614
615        """
616        super(OnDemandCredentials, self).__init__()
617        self._signer = signer
618        self._issuer = issuer
619        self._subject = subject
620        self._token_lifetime = token_lifetime
621        self._quota_project_id = quota_project_id
622
623        if additional_claims is None:
624            additional_claims = {}
625
626        self._additional_claims = additional_claims
627        self._cache = cachetools.LRUCache(maxsize=max_cache_size)
628
629    @classmethod
630    def _from_signer_and_info(cls, signer, info, **kwargs):
631        """Creates an OnDemandCredentials instance from a signer and service
632        account info.
633
634        Args:
635            signer (google.auth.crypt.Signer): The signer used to sign JWTs.
636            info (Mapping[str, str]): The service account info.
637            kwargs: Additional arguments to pass to the constructor.
638
639        Returns:
640            google.auth.jwt.OnDemandCredentials: The constructed credentials.
641
642        Raises:
643            ValueError: If the info is not in the expected format.
644        """
645        kwargs.setdefault("subject", info["client_email"])
646        kwargs.setdefault("issuer", info["client_email"])
647        return cls(signer, **kwargs)
648
649    @classmethod
650    def from_service_account_info(cls, info, **kwargs):
651        """Creates an OnDemandCredentials instance from a dictionary.
652
653        Args:
654            info (Mapping[str, str]): The service account info in Google
655                format.
656            kwargs: Additional arguments to pass to the constructor.
657
658        Returns:
659            google.auth.jwt.OnDemandCredentials: The constructed credentials.
660
661        Raises:
662            ValueError: If the info is not in the expected format.
663        """
664        signer = _service_account_info.from_dict(info, require=["client_email"])
665        return cls._from_signer_and_info(signer, info, **kwargs)
666
667    @classmethod
668    def from_service_account_file(cls, filename, **kwargs):
669        """Creates an OnDemandCredentials instance from a service account .json
670        file in Google format.
671
672        Args:
673            filename (str): The path to the service account .json file.
674            kwargs: Additional arguments to pass to the constructor.
675
676        Returns:
677            google.auth.jwt.OnDemandCredentials: The constructed credentials.
678        """
679        info, signer = _service_account_info.from_filename(
680            filename, require=["client_email"]
681        )
682        return cls._from_signer_and_info(signer, info, **kwargs)
683
684    @classmethod
685    def from_signing_credentials(cls, credentials, **kwargs):
686        """Creates a new :class:`google.auth.jwt.OnDemandCredentials` instance
687        from an existing :class:`google.auth.credentials.Signing` instance.
688
689        The new instance will use the same signer as the existing instance and
690        will use the existing instance's signer email as the issuer and
691        subject by default.
692
693        Example::
694
695            svc_creds = service_account.Credentials.from_service_account_file(
696                'service_account.json')
697            jwt_creds = jwt.OnDemandCredentials.from_signing_credentials(
698                svc_creds)
699
700        Args:
701            credentials (google.auth.credentials.Signing): The credentials to
702                use to construct the new credentials.
703            kwargs: Additional arguments to pass to the constructor.
704
705        Returns:
706            google.auth.jwt.Credentials: A new Credentials instance.
707        """
708        kwargs.setdefault("issuer", credentials.signer_email)
709        kwargs.setdefault("subject", credentials.signer_email)
710        return cls(credentials.signer, **kwargs)
711
712    def with_claims(self, issuer=None, subject=None, additional_claims=None):
713        """Returns a copy of these credentials with modified claims.
714
715        Args:
716            issuer (str): The `iss` claim. If unspecified the current issuer
717                claim will be used.
718            subject (str): The `sub` claim. If unspecified the current subject
719                claim will be used.
720            additional_claims (Mapping[str, str]): Any additional claims for
721                the JWT payload. This will be merged with the current
722                additional claims.
723
724        Returns:
725            google.auth.jwt.OnDemandCredentials: A new credentials instance.
726        """
727        new_additional_claims = copy.deepcopy(self._additional_claims)
728        new_additional_claims.update(additional_claims or {})
729
730        return self.__class__(
731            self._signer,
732            issuer=issuer if issuer is not None else self._issuer,
733            subject=subject if subject is not None else self._subject,
734            additional_claims=new_additional_claims,
735            max_cache_size=self._cache.maxsize,
736            quota_project_id=self._quota_project_id,
737        )
738
739    @_helpers.copy_docstring(google.auth.credentials.CredentialsWithQuotaProject)
740    def with_quota_project(self, quota_project_id):
741
742        return self.__class__(
743            self._signer,
744            issuer=self._issuer,
745            subject=self._subject,
746            additional_claims=self._additional_claims,
747            max_cache_size=self._cache.maxsize,
748            quota_project_id=quota_project_id,
749        )
750
751    @property
752    def valid(self):
753        """Checks the validity of the credentials.
754
755        These credentials are always valid because it generates tokens on
756        demand.
757        """
758        return True
759
760    def _make_jwt_for_audience(self, audience):
761        """Make a new JWT for the given audience.
762
763        Args:
764            audience (str): The intended audience.
765
766        Returns:
767            Tuple[bytes, datetime]: The encoded JWT and the expiration.
768        """
769        now = _helpers.utcnow()
770        lifetime = datetime.timedelta(seconds=self._token_lifetime)
771        expiry = now + lifetime
772
773        payload = {
774            "iss": self._issuer,
775            "sub": self._subject,
776            "iat": _helpers.datetime_to_secs(now),
777            "exp": _helpers.datetime_to_secs(expiry),
778            "aud": audience,
779        }
780
781        payload.update(self._additional_claims)
782
783        jwt = encode(self._signer, payload)
784
785        return jwt, expiry
786
787    def _get_jwt_for_audience(self, audience):
788        """Get a JWT For a given audience.
789
790        If there is already an existing, non-expired token in the cache for
791        the audience, that token is used. Otherwise, a new token will be
792        created.
793
794        Args:
795            audience (str): The intended audience.
796
797        Returns:
798            bytes: The encoded JWT.
799        """
800        token, expiry = self._cache.get(audience, (None, None))
801
802        if token is None or expiry < _helpers.utcnow():
803            token, expiry = self._make_jwt_for_audience(audience)
804            self._cache[audience] = token, expiry
805
806        return token
807
808    def refresh(self, request):
809        """Raises an exception, these credentials can not be directly
810        refreshed.
811
812        Args:
813            request (Any): Unused.
814
815        Raises:
816            google.auth.RefreshError
817        """
818        # pylint: disable=unused-argument
819        # (pylint doesn't correctly recognize overridden methods.)
820        raise exceptions.RefreshError(
821            "OnDemandCredentials can not be directly refreshed."
822        )
823
824    def before_request(self, request, method, url, headers):
825        """Performs credential-specific before request logic.
826
827        Args:
828            request (Any): Unused. JWT credentials do not need to make an
829                HTTP request to refresh.
830            method (str): The request's HTTP method.
831            url (str): The request's URI. This is used as the audience claim
832                when generating the JWT.
833            headers (Mapping): The request's headers.
834        """
835        # pylint: disable=unused-argument
836        # (pylint doesn't correctly recognize overridden methods.)
837        parts = urllib.parse.urlsplit(url)
838        # Strip query string and fragment
839        audience = urllib.parse.urlunsplit(
840            (parts.scheme, parts.netloc, parts.path, "", "")
841        )
842        token = self._get_jwt_for_audience(audience)
843        self.apply(headers, token=token)
844
845    @_helpers.copy_docstring(google.auth.credentials.Signing)
846    def sign_bytes(self, message):
847        return self._signer.sign(message)
848
849    @property
850    @_helpers.copy_docstring(google.auth.credentials.Signing)
851    def signer_email(self):
852        return self._issuer
853
854    @property
855    @_helpers.copy_docstring(google.auth.credentials.Signing)
856    def signer(self):
857        return self._signer
858