169 lines
5.9 KiB
Python
169 lines
5.9 KiB
Python
|
# Copyright 2020 Google LLC
|
||
|
#
|
||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
# you may not use this file except in compliance with the License.
|
||
|
# You may obtain a copy of the License at
|
||
|
#
|
||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||
|
#
|
||
|
# Unless required by applicable law or agreed to in writing, software
|
||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
# See the License for the specific language governing permissions and
|
||
|
# limitations under the License.
|
||
|
|
||
|
"""JSON Web Tokens
|
||
|
|
||
|
Provides support for creating (encoding) and verifying (decoding) JWTs,
|
||
|
especially JWTs generated and consumed by Google infrastructure.
|
||
|
|
||
|
See `rfc7519`_ for more details on JWTs.
|
||
|
|
||
|
To encode a JWT use :func:`encode`::
|
||
|
|
||
|
from google.auth import crypt
|
||
|
from google.auth import jwt_async
|
||
|
|
||
|
signer = crypt.Signer(private_key)
|
||
|
payload = {'some': 'payload'}
|
||
|
encoded = jwt_async.encode(signer, payload)
|
||
|
|
||
|
To decode a JWT and verify claims use :func:`decode`::
|
||
|
|
||
|
claims = jwt_async.decode(encoded, certs=public_certs)
|
||
|
|
||
|
You can also skip verification::
|
||
|
|
||
|
claims = jwt_async.decode(encoded, verify=False)
|
||
|
|
||
|
.. _rfc7519: https://tools.ietf.org/html/rfc7519
|
||
|
|
||
|
|
||
|
NOTE: This async support is experimental and marked internal. This surface may
|
||
|
change in minor releases.
|
||
|
"""
|
||
|
|
||
|
import google.auth
|
||
|
from google.auth import jwt
|
||
|
|
||
|
|
||
|
def encode(signer, payload, header=None, key_id=None):
|
||
|
"""Make a signed JWT.
|
||
|
|
||
|
Args:
|
||
|
signer (google.auth.crypt.Signer): The signer used to sign the JWT.
|
||
|
payload (Mapping[str, str]): The JWT payload.
|
||
|
header (Mapping[str, str]): Additional JWT header payload.
|
||
|
key_id (str): The key id to add to the JWT header. If the
|
||
|
signer has a key id it will be used as the default. If this is
|
||
|
specified it will override the signer's key id.
|
||
|
|
||
|
Returns:
|
||
|
bytes: The encoded JWT.
|
||
|
"""
|
||
|
return jwt.encode(signer, payload, header, key_id)
|
||
|
|
||
|
|
||
|
def decode(token, certs=None, verify=True, audience=None):
|
||
|
"""Decode and verify a JWT.
|
||
|
|
||
|
Args:
|
||
|
token (str): The encoded JWT.
|
||
|
certs (Union[str, bytes, Mapping[str, Union[str, bytes]]]): The
|
||
|
certificate used to validate the JWT signature. If bytes or string,
|
||
|
it must the the public key certificate in PEM format. If a mapping,
|
||
|
it must be a mapping of key IDs to public key certificates in PEM
|
||
|
format. The mapping must contain the same key ID that's specified
|
||
|
in the token's header.
|
||
|
verify (bool): Whether to perform signature and claim validation.
|
||
|
Verification is done by default.
|
||
|
audience (str): The audience claim, 'aud', that this JWT should
|
||
|
contain. If None then the JWT's 'aud' parameter is not verified.
|
||
|
|
||
|
Returns:
|
||
|
Mapping[str, str]: The deserialized JSON payload in the JWT.
|
||
|
|
||
|
Raises:
|
||
|
ValueError: if any verification checks failed.
|
||
|
"""
|
||
|
|
||
|
return jwt.decode(token, certs, verify, audience)
|
||
|
|
||
|
|
||
|
class Credentials(
|
||
|
jwt.Credentials,
|
||
|
google.auth._credentials_async.Signing,
|
||
|
google.auth._credentials_async.Credentials,
|
||
|
):
|
||
|
"""Credentials that use a JWT as the bearer token.
|
||
|
|
||
|
These credentials require an "audience" claim. This claim identifies the
|
||
|
intended recipient of the bearer token.
|
||
|
|
||
|
The constructor arguments determine the claims for the JWT that is
|
||
|
sent with requests. Usually, you'll construct these credentials with
|
||
|
one of the helper constructors as shown in the next section.
|
||
|
|
||
|
To create JWT credentials using a Google service account private key
|
||
|
JSON file::
|
||
|
|
||
|
audience = 'https://pubsub.googleapis.com/google.pubsub.v1.Publisher'
|
||
|
credentials = jwt_async.Credentials.from_service_account_file(
|
||
|
'service-account.json',
|
||
|
audience=audience)
|
||
|
|
||
|
If you already have the service account file loaded and parsed::
|
||
|
|
||
|
service_account_info = json.load(open('service_account.json'))
|
||
|
credentials = jwt_async.Credentials.from_service_account_info(
|
||
|
service_account_info,
|
||
|
audience=audience)
|
||
|
|
||
|
Both helper methods pass on arguments to the constructor, so you can
|
||
|
specify the JWT claims::
|
||
|
|
||
|
credentials = jwt_async.Credentials.from_service_account_file(
|
||
|
'service-account.json',
|
||
|
audience=audience,
|
||
|
additional_claims={'meta': 'data'})
|
||
|
|
||
|
You can also construct the credentials directly if you have a
|
||
|
:class:`~google.auth.crypt.Signer` instance::
|
||
|
|
||
|
credentials = jwt_async.Credentials(
|
||
|
signer,
|
||
|
issuer='your-issuer',
|
||
|
subject='your-subject',
|
||
|
audience=audience)
|
||
|
|
||
|
The claims are considered immutable. If you want to modify the claims,
|
||
|
you can easily create another instance using :meth:`with_claims`::
|
||
|
|
||
|
new_audience = (
|
||
|
'https://pubsub.googleapis.com/google.pubsub.v1.Subscriber')
|
||
|
new_credentials = credentials.with_claims(audience=new_audience)
|
||
|
"""
|
||
|
|
||
|
|
||
|
class OnDemandCredentials(
|
||
|
jwt.OnDemandCredentials,
|
||
|
google.auth._credentials_async.Signing,
|
||
|
google.auth._credentials_async.Credentials,
|
||
|
):
|
||
|
"""On-demand JWT credentials.
|
||
|
|
||
|
Like :class:`Credentials`, this class uses a JWT as the bearer token for
|
||
|
authentication. However, this class does not require the audience at
|
||
|
construction time. Instead, it will generate a new token on-demand for
|
||
|
each request using the request URI as the audience. It caches tokens
|
||
|
so that multiple requests to the same URI do not incur the overhead
|
||
|
of generating a new token every time.
|
||
|
|
||
|
This behavior is especially useful for `gRPC`_ clients. A gRPC service may
|
||
|
have multiple audience and gRPC clients may not know all of the audiences
|
||
|
required for accessing a particular service. With these credentials,
|
||
|
no knowledge of the audiences is required ahead of time.
|
||
|
|
||
|
.. _grpc: http://www.grpc.io/
|
||
|
"""
|