#python #jwt #google-cloud-functions #google-oauth
# #python #jwt #google-cloud-функции #google-oauth
Вопрос:
Я пытаюсь добавить авторизацию в свои облачные функции python. Я создал учетную запись службы в проекте GCP и сгенерировал ключи. Тестовый клиентский код (не в GCP) для вызова облачной функции выглядит следующим образом:
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')
Я знаю, что этот код правильно получает токен-носитель JWT от Google и добавляется в заголовок авторизации при вызове моей облачной функции. Мне просто сложно проверить этот токен в облачной функции. Соответствующая часть этого кода выглядит следующим образом:
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())
Я знаю, что токен id правильный, потому что проверка вручную (с использованием https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=xxx ) возвращает именно то, что я ожидал бы.
Я получаю трассировку стека при регистрации ошибок:
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'xc9xadxbd'
Чего мне здесь не хватает? Спасибо
Комментарии:
1. Из чего состоят первые 10 символов
request.headers.get('Authorization')[7:]
? Проблема выглядит так, как будто вы неправильно захватываете токен, и декодирование не выполняется.2. Заголовок авторизации
Bearer ya29.c.EljxBqdHSeW1IQc....
снова, если я беру это значение (минус обозначение ‘Bearer’) и помещаю его в googleapis.com/oauth2/v1/tokeninfo?access_token=xxx он проверяет его правильно, поэтому я не уверен, чего мне не хватает в «автономном» коде валидатора.3. Смогли ли вы решить эту проблему? Я тоже сталкиваюсь с этим.
Ответ №1:
Установив GOOGLE_APPLICATION_CREDENTIAL
переменную среды в вашей локальной системе, ваш клиент будет работать в контексте этой учетной записи службы, не беспокоясь об авторизации. Вам не нужно кодировать путь к файлу ключей.
Это также относится к развертыванию облачной функции и ее локальному тестированию. Когда вы развертываете облачную функцию, она запускается как учетная запись службы AppEngine по умолчанию или учетная запись службы, которую вы указываете с помощью параметра —service-account : https://cloud.google.com/sdk/gcloud/reference/functions/deploy
Ссылка: https://cloud.google.com/docs/authentication/production
Таким образом, вам не нужно передавать ключ на сервер или беспокоиться об этом в git, а также вам не нужно вносить какие-либо изменения в код при локальном или удаленном запуске.
Комментарии:
1. Пропустил, что это тестовый клиент, хотя применяется тот же принцип, но взгляните на этот тестовый клиент: cloud.google.com/functions/docs/bestpractices/… Это для функций, запускаемых хранилищем, но также есть пример для pubsub.
2. Я думаю, что более важный вопрос заключается в том, зачем использовать облачные функции для приема трафика, который должен быть аутентифицирован? Google не предоставляет никакой документации по аутентификации клиентов для http-функций, отличных от CORS, в то время как они предоставляют несколько других простых в использовании инструментов, которые соответствовали бы требованиям, таким как Pub / Sub и CloudEndpoints, где аутентификация так же проста, как get_user или конфигурация.
3. Я не согласен, программирование — это решение проблем, и часто есть много способов решить проблему. В данном случае я предлагаю другое решение — облачные конечные точки или Pub / Sub, оба из которых могли бы решить проблему. Теперь, если вы хотите ответить на его вопрос о том, как проверить access_token в http-функции, вместо того, чтобы критиковать меня, продолжайте.
Ответ №2:
Убедитесь, что в строке, переданной в id_token.verify_oauth2_token()
, все еще нет «носителя» в начале.