Google app engine 向受IAP保护的应用程序请求服务帐户将导致';无效的GCIP ID令牌:JWT签名无效';

Google app engine 向受IAP保护的应用程序请求服务帐户将导致';无效的GCIP ID令牌:JWT签名无效';,google-app-engine,google-cloud-platform,google-api-python-client,google-iap,google-cloud-identity,Google App Engine,Google Cloud Platform,Google Api Python Client,Google Iap,Google Cloud Identity,我试图通过Python从GCP环境外部以编程方式访问受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): &qu

我试图通过Python从GCP环境外部以编程方式访问受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)
    print(f'{open_id_connect_token=}')

    # 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)
    print(f'{resp=}')
    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

if __name__ == '__main__':
    res = make_iap_request(
        'https://MYAPP.ue.r.appspot.com/',
        'Client ID from IAP>App Engine app>Edit OAuth Client>Client ID'
        )
    print(res)

当我在本地运行它时,我将GOOGLE_APPLICATION_CREDENTIALS环境变量设置为本地JSON凭据文件,其中包含我要使用的服务帐户的密钥。我也尝试过在云中运行这个功能,所以它可能会使用元数据服务来获取应用引擎的默认服务帐户(我想是吧?)

在这两种情况下,我都能够生成一个看起来有效的令牌。使用jwt.io,我看到它包含预期的数据,并且签名有效。但是,当我使用令牌向应用程序发出请求时,总是会出现以下异常:

应用程序的错误响应:401/{'X-Goog-IAP-Generated-response':'true','Date':'Tue,09 Feb 2021 19:25:43 GMT','Content Type':'text/html','Server':'Google Frontend','Content Length':'47','Alt Svc':'h3-29=“:443;ma=2592000,h3-T051=“:443;ma=2592000,h3-Q050=“:443”;ma=2592000,h3-Q043=“”;ma=2592000,quic=“:443”;ma=2592000;v=“46,43”}/'无效的GCIP ID令牌:JWT签名无效'


我可能做错了什么?

此问题的解决方案是将Google标识令牌交换为Identity Platform标识令牌

错误原因
无效的GCIP ID令牌:JWT签名无效
是由于使用由Google RSA私钥而非Google Identity Platform RSA私钥签名的Google Identity令牌造成的。我忽略了错误消息中的
GCIP
,一旦我们验证令牌在使用中没有损坏,它就会告诉我解决方案

在问题中,这行代码获取Google标识:

open_id_connect_token = id_token.fetch_id_token(Request(), client_id)
以上代码行要求设置Google云应用程序默认凭据。示例:
set GOOGLE\u APPLICATION\u CREDENTIALS=c:\config\service account.json

下一步是将此令牌交换为Identity Platform令牌:

def exchange_google_id_token_for_gcip_id_token(google_open_id_connect_token):
    SIGN_IN_WITH_IDP_API = 'https://identitytoolkit.googleapis.com/v1/accounts:signInWithIdp'
    API_KEY = '';

    url = SIGN_IN_WITH_IDP_API + '?key=' + API_KEY;

    data={
        'requestUri': 'http://localhost',
        'returnSecureToken': True,
        'postBody':'id_token=' + google_open_id_connect_token + '&providerId=google.com'}

    try:
        resp = requests.post(url, data)

        res = resp.json()

        if 'error' in res:
            print("Error: {}".format(res['error']['message']))
            exit(1)

        # print(res)

        return res['idToken']
    except Exception as ex:
        print("Exception: {}".format(ex))
        exit(1)
API密钥可以在Google云控制台->Identity Platform中找到。右上角的“应用程序设置详细信息”。这将显示
apiKey
authDomain

有关更多信息,请访问此链接:


解决此问题的方法是将Google标识令牌交换为Identity Platform标识令牌

错误原因
无效的GCIP ID令牌:JWT签名无效
是由于使用由Google RSA私钥而非Google Identity Platform RSA私钥签名的Google Identity令牌造成的。我忽略了错误消息中的
GCIP
,一旦我们验证令牌在使用中没有损坏,它就会告诉我解决方案

在问题中,这行代码获取Google标识:

open_id_connect_token = id_token.fetch_id_token(Request(), client_id)
以上代码行要求设置Google云应用程序默认凭据。示例:
set GOOGLE\u APPLICATION\u CREDENTIALS=c:\config\service account.json

下一步是将此令牌交换为Identity Platform令牌:

def exchange_google_id_token_for_gcip_id_token(google_open_id_connect_token):
    SIGN_IN_WITH_IDP_API = 'https://identitytoolkit.googleapis.com/v1/accounts:signInWithIdp'
    API_KEY = '';

    url = SIGN_IN_WITH_IDP_API + '?key=' + API_KEY;

    data={
        'requestUri': 'http://localhost',
        'returnSecureToken': True,
        'postBody':'id_token=' + google_open_id_connect_token + '&providerId=google.com'}

    try:
        resp = requests.post(url, data)

        res = resp.json()

        if 'error' in res:
            print("Error: {}".format(res['error']['message']))
            exit(1)

        # print(res)

        return res['idToken']
    except Exception as ex:
        print("Exception: {}".format(ex))
        exit(1)
API密钥可以在Google云控制台->Identity Platform中找到。右上角的“应用程序设置详细信息”。这将显示
apiKey
authDomain

有关更多信息,请访问此链接:


您是在创建访问令牌还是身份令牌?用您编写的代码编辑您的问题(链接不可以,除非作为附加参考)。显示您如何向应用程序引擎发出请求。提示。IAP需要HTTP头中的身份令牌
Authorization:Bearer
。错误
JWT签名无效
意味着您在API/REST调用中使用令牌的方式可能有错误。感谢您的发布提示,@JohnHanley。我已经用代码和一些其他详细信息更新了我的问题。@JohnHanley为了回答您最初的问题,我正在尝试生成一个标识令牌,但我不清楚在这种情况下访问令牌在哪里起作用。我是否缺少一个步骤,需要先生成一个访问令牌?代码中此语句的输出是什么(仅前10个字符):
print(f'{open\u id\u connect\u token=}')
?我感觉您正在将IAP>App Engine App>Edit OAuth Client>Client ID中的字符串
Client ID追加到标识令牌。如果为true,请从
标题={'Authorization':'Bearer{}.format(open_id_connect_token)},**kwargs)中删除
kwargs
,感谢您的时间和见解。最终,@JohnHanley确定的解决方案是为Identity Platform令牌()执行令牌交换。您是在创建访问令牌还是身份令牌?用您编写的代码编辑您的问题(链接不可以,除非作为附加参考)。显示您如何向应用程序引擎发出请求。提示。IAP需要HTTP头中的身份令牌
Authorization:Bearer
。错误
JWT签名无效
意味着您在API/REST调用中使用令牌的方式可能有错误。感谢您的发布提示,@JohnHanley。我已经用代码和一些其他详细信息更新了我的问题。@JohnHanley为了回答您最初的问题,我正在尝试生成一个标识令牌,但我不清楚在这种情况下访问令牌在哪里起作用。我是否缺少一个步骤,需要先生成一个访问令牌?代码中此语句的输出是什么(仅前10个字符):
print(f'{open\u id\u connect\u token=}')
?我感觉您正在从IAP>应用程序中添加字符串
客户端ID