无法 POST 使用 Python 中的服务帐户密钥文件进行请求,获取 'Invalid IAP credentials: Unable to parse JWT',“401 状态代码”

Cannot POST request using service account key file in Python, getting 'Invalid IAP credentials: Unable to parse JWT', '401 Status Code'

我正在尝试向 Google App Engine 服务发送 POST 请求,其中包含 JSON body 和授权令牌。我正在从本地服务帐户密钥 JSON 文件生成访问令牌。下面的代码正在生成凭据,但最终授权被拒绝。我也已经尝试过不同的方法。甚至尝试在 Header 中使用 Bearer 令牌在 Postman 中编写请求,或者甚至作为普通的 cURL 命令。但无论我尝试什么,都会出现 401 身份验证错误。我需要确定问题是在我这边还是在服务的另一边。探索了所有可用的文档,但没有成功。

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

CREDENTIAL_SCOPES = ["https://www.googleapis.com/auth/cloud-platform"]
CREDENTIALS_KEY_PATH = 'my-local-service-account-key-file.json'

#the example service url I am trying to hit with requests
url = 'https://test.appspot.com/submit'

headers = {"Content-Type": "application/json"}

#example data I am sending with the request body
payload = {
  "key1": "value 1",
  "key2": "value 2"
}


credentials = service_account.Credentials.from_service_account_file(
                    CREDENTIALS_KEY_PATH,
                    scopes=CREDENTIAL_SCOPES
                    )

credentials.refresh(requests.Request())

authed_session = AuthorizedSession(credentials)

response = authed_session.request('POST',
                                  url,
                                  headers=headers,
                                  data=payload
                                )

#adding some debug lines for your help
print(response.text)
print(response.status_code)
print(response.headers)

获取输出:

Invalid IAP credentials: Unable to parse JWT
401
{'X-Goog-IAP-Generated-Response': 'true', 'Date': 'Mon, 03 May 2021 06:52:11 GMT', 'Content-Type': 'text/html', 'Server': 'Google Frontend', 'Content-Length': '44', 'Alt-Svc': 'h3-29=":443"; ma=2592000,h3-T051=":443"; ma=2592000,h3-Q050=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43"'}

IAP 期望在 Authorization header 中有一个 JWT(OpenID Connect (OIDC)) token,而您的方法将在 Authorization header 中附加一个访问令牌。查看下面的代码片段以向 IAP 安全资源发出请求。

您的代码需要如下所示:

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


def make_iap_request(url, client_id, method='GET', **kwargs):
    """Makes a request to an application protected by Identity-Aware Proxy.

    Args:
      url: The Identity-Aware Proxy-protected URL to fetch.
      client_id: The client ID used by Identity-Aware Proxy.
      method: The request method to use
              ('GET', 'OPTIONS', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE')
      **kwargs: Any of the parameters defined for the request function:
                https://github.com/requests/requests/blob/master/requests/api.py
                If no timeout is provided, it is set to 90 by default.

    Returns:
      The page body, or raises an exception if the page couldn't be retrieved.
    """
    # Set the default timeout, if missing
    if 'timeout' not in kwargs:
        kwargs['timeout'] = 90

    # Obtain an OpenID Connect (OIDC) token from metadata server or using service
    # account.
    open_id_connect_token = id_token.fetch_id_token(Request(), client_id)

    # Fetch the Identity-Aware Proxy-protected URL, including an
    # Authorization header containing "Bearer " followed by a
    # Google-issued OpenID Connect token for the service account.
    resp = requests.request(
        method, url,
        headers={'Authorization': 'Bearer {}'.format(
            open_id_connect_token)}, **kwargs)
    if resp.status_code == 403:
        raise Exception('Service account does not have permission to '
                        'access the IAP-protected application.')
    elif resp.status_code != 200:
        raise Exception(
            'Bad response from application: {!r} / {!r} / {!r}'.format(
                resp.status_code, resp.headers, resp.text))
    else:
        return resp.text

注意:上述方法适用于可以通过 运行ning 命令设置的隐式凭据:export GOOGLE_APPLICATION_CREDENTIALS=my-local-service-account-key-file.json 在环境中设置您的服务帐户的路径,然后 运行来自同一终端的 python 代码。

查看 this link 了解更多信息。