Как я могу использовать Firebase в Google App Engine с несколькими Сервисами? - PullRequest
1 голос
/ 07 апреля 2020

У меня есть приложение GAE на Python 2.7 (пожалуйста, не спрашивайте, почему) с двумя сервисами: UI & API (ie Я использую dispatch.yaml для развертывания нескольких сервисов, каждый в своих вложенных папках со своими собственными app.yaml). У меня есть конечная точка API для создания подписанных URL-адресов для интерфейса пользователя, который используется для загрузки файлов в GCS. В качестве уровня безопасности я хочу проверять пользователей API, используя ту же систему, в которой используется пользовательский интерфейс: Firebase.

И в пользовательском интерфейсе, и в API у меня есть вспомогательная функция, которая:

  1. Загружает сессионный повар Firebase ie для документации по настройке администратора Firebase
  2. Проверяет его и извлекает «утверждения» (информация о проверенном пользователе)
  3. UI / API затем использовать заявки для обработки остальной части веб-запроса / ответа

Однако даже с одинаковым кодом в пользовательском интерфейсе и API (и одинаковыми настройками в app.yaml), которые являются частью одного и того же проекта GAE, Firebase выдает ошибку в API при вызове auth.verify_session_cookie(session_cookie, check_revoked=True), но работает как чемпион в коде пользовательского интерфейса.

Настройка:

Вот соответствующие части каждого файла, найденные как в пользовательском интерфейсе, так и в API Подпапки кода служб

app.yaml

# The Service names are set as appropriate in respective service app.yaml's:
# service: api
# service: default
...
env_variables:
  # Replace with your Firebase project ID.
  FIREBASE_PROJECT_ID: 'my-rad-project'
  GCLOUD_PROJECT: 'my-rad-project'  # this env var doesn't seem to be necessary to set to make Firebase work on UI

main.py

Интерфейс пользователя устанавливает повара ie в коде, показанном ниже. Этот код здесь проверяет повара ie и извлекает утверждения, если это возможно:

import requests_toolbelt.adapters.appengine
...
from firebase_admin import auth, initialize_app # This must go after the monkeypatch()
default_app = initialize_app()  # "[DEFAULT]"
...
def get_claims():
    session_cookie = request.cookies.get(COOKIE_NAME)
    # Verify the session cookie. In this case an additional check is added to detect
    # if the user's Firebase session was revoked, user deleted/disabled, etc.
    decoded_claims = auth.verify_session_cookie(session_cookie, check_revoked=True)
    return decoded_claims

Существует также конечная точка flask для установки повара ie COOKIE_NAME из idToken, предоставленного firebase. Это существует только в службе пользовательского интерфейса, но оно размещается на том же поддомене, что и API (конечные точки API просто имеют /api, начинающие свои URL-адреса)

@app.route('/sessionLogin', methods=['POST'])
def session_login():
    id_token = request.json['idToken']
    # To ensure that cookies are set only on recently signed in users, check auth_time in
    # ID token before creating a cookie.
    try:
        decoded_claims = auth.verify_id_token(id_token, check_revoked=True)
        # Only process if the user signed in within the last 5 days.
        if time.time() - decoded_claims['auth_time'] < 5 * 24 * 60 * 60:
            expires_in = datetime.timedelta(days=5)
            expires = datetime.datetime.now() + expires_in
            session_cookie = auth.create_session_cookie(id_token, expires_in=expires_in)
            response = jsonify({COOKIE_NAME: session_cookie})
            response.set_cookie(
                COOKIE_NAME, session_cookie, expires=expires, httponly=True, secure=True, domain='.my.domain')
            return response
        # User did not sign in recently. To guard against ID token theft, require
        # re-authentication.
        err = 'Recent sign in required'
        log.error(err)
        return abort(401, err)
    except ValueError:
        err = 'Invalid ID token'
        log.error(err)
        return abort(401, err)
    except Exception as e:
        # Session revoked. Force user to login.
        err = "Firebase exception getting claims: {}".format(e)
        log.error(err)
        return abort(401, err)

Ошибки

Когда я отправьте веб-запрос конечной точке API, которая обрабатывает подпись GCS, сначала она вызывает get_claims(), а затем выдает эту ошибку:

The credential used to initialize the SDK has insufficient permissions to perform the requested operation. See https://firebase.google.com/docs/admin/setup for details on how to initialize the Admin SDK with appropriate permissions (INSUFFICIENT_PERMISSION).

Исправления I Tried

Я следовал Firebase документы для настройки его для нескольких приложений , считая, что, поскольку мой пользовательский интерфейс является службой "по умолчанию" в моем приложении GAE, возможно, мне нужно было инициализировать приложение API, которое называется службой "api" в моем app.yaml. Это не помогло:

# In API's main.py
default_app = initialize_app()
api_app = initialize_app(name='api')

Затем я использовал собственный слой авторизации Google , который должен работать с Firebase:

# API's main.py
from google.oauth2.id_token import verify_firebase_token
from google.auth.transport.requests import Request

HTTP_REQUEST = Request()
...
# Change 1 line in `get_claims()` like so:
decoded_claims = verify_firebase_token(session_cookie, HTTP_REQUEST)

Это привело к новому ошибка: Certificate for key id asdf1234 not found. Я нашел , казалось бы, связанный вопрос SO , но его решение не сработало (у меня есть веб-страница, а не приложение Android, и даже если я пытаюсь в другом браузере, вынуждает меня войдите с Google в новый сеанс, сервер по-прежнему выдает ту же ошибку)

Все это время пользовательский интерфейс работает отлично, чтобы запустить get_claims(). Что мне нужно сделать, чтобы использовать проверку подлинности Firebase в моей службе API с использованием значения cook ie, установленного моей службой пользовательского интерфейса, оба из которых находятся на одном поддомене и имеют одинаковые переменные среды, установленные в их файлах app.yaml?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...