'Trouble validating JWT access token in python cloud function

I am trying to add authorization to my python Cloud Functions. I created a service account in the GCP project and generated keys. The test client code (not in GCP) to call the Cloud Function looks like this:

from google.oauth2 import service_account
from google.auth.transport.requests import AuthorizedSession

SERVICE_ACCOUNT_FILE = '<my_project_key_file>.json'

credentials = service_account.Credentials.from_service_account_file(SERVICE_ACCOUNT_FILE, 
scopes=['https://www.googleapis.com/auth/userinfo.email'])
authed_session = AuthorizedSession(credentials)
response = authed_session.get('https://<my_project>.cloudfunctions.net/authValidation')

I know this code correctly gets the JWT bearer token from Google and is added to the Authorization header in the call to my Cloud Function. I'm just having a hard time validating that token in the Cloud Function. The relevant part of that code looks like this:

from google.oauth2 import id_token
from google.auth.transport import requests

def hello_world(request):

    #  from https://developers.google.com/identity/sign-in/web/backend-auth#using-a-google-api-client-library    
    idinfo = id_token.verify_oauth2_token(request.headers.get('Authorization')[7:]), requests.Request())

I know the id token is correct because the manual validation (using https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=xxx ) returns exactly what I would expect.

The error logging stacktrace I get is:

Traceback (most recent call last):
  File "/env/local/lib/python3.7/site-packages/google/cloud/functions/worker.py", line 346, in run_http_function
    result = _function_handler.invoke_user_function(flask.request)
  File "/env/local/lib/python3.7/site-packages/google/cloud/functions/worker.py", line 217, in invoke_user_function
    return call_user_function(request_or_event)
  File "/env/local/lib/python3.7/site-packages/google/cloud/functions/worker.py", line 210, in call_user_function
    return self._user_function(request_or_event)
  File "/user_code/main.py", line 17, in hello_world
    idinfo = id_token.verify_oauth2_token(request.headers.get('Authorization')[7:], requests.Request())
  File "/env/local/lib/python3.7/site-packages/google/oauth2/id_token.py", line 141, in verify_oauth2_token
    certs_url=_GOOGLE_OAUTH2_CERTS_URL)
  File "/env/local/lib/python3.7/site-packages/google/oauth2/id_token.py", line 122, in verify_token
    return jwt.decode(id_token, certs=certs, audience=audience)
  File "/env/local/lib/python3.7/site-packages/google/auth/jwt.py", line 219, in decode
    header, payload, signed_section, signature = _unverified_decode(token)
  File "/env/local/lib/python3.7/site-packages/google/auth/jwt.py", line 139, in _unverified_decode
    header = _decode_jwt_segment(encoded_header)
  File "/env/local/lib/python3.7/site-packages/google/auth/jwt.py", line 112, in _decode_jwt_segment
    six.raise_from(new_exc, caught_exc)
  File "<string>", line 3, in raise_from
ValueError: Can't parse segment: b'\xc9\xad\xbd'

What am I missing here? Thanks



Solution 1:[1]

By setting the GOOGLE_APPLICATION_CREDENTIAL environment variable in your local system, your client will run under the context of that service account without having to worry about auth. You don't need to code the path to the keyfile.

Also applies to deploying the Cloud Function, and testing it locally. When you deploy a Cloud Function, it runs as the AppEngine default service account, or the service account you specify with the --service-account parameter: https://cloud.google.com/sdk/gcloud/reference/functions/deploy

Reference: https://cloud.google.com/docs/authentication/production

This way you don't need to push the key to the server or worry about it in git, and you also don't need to make any code changes while running locally vs remotely.

Solution 2:[2]

Make sure the string passed to id_token.verify_oauth2_token() does not have the "Bearer " still at the start of it.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 JGC
Solution 2 Brian C.