简体   繁体   English

对受 IAP 保护的应用程序的服务帐户请求导致“无效的 GCIP ID 令牌:JWT 签名无效”

[英]Service account request to IAP-protected app results in 'Invalid GCIP ID token: JWT signature is invalid'

I am trying to programmatically access an IAP-protected App Engine Standard app via Python from outside of the GCP environment.我正在尝试从 GCP 环境外部通过 Python 以编程方式访问受 IAP 保护的 App Engine 标准应用程序。 I have tried various methods, including the method shown in the docs here: https://cloud.google.com/iap/docs/authentication-howto#iap-make-request-python .我尝试了各种方法,包括此处文档中显示的方法: https://cloud.google.com/iap/docs/authentication-howto#iap-make-request-python Here is my code:这是我的代码:

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)

When I run it locally, I have the GOOGLE_APPLICATION_CREDENTIALS environment variable set to a local JSON credential file containing the keys for the service account I want to use.当我在本地运行它时,我将 GOOGLE_APPLICATION_CREDENTIALS 环境变量设置为本地 JSON 凭据文件,其中包含我要使用的服务帐户的密钥。 I have also tried running this in Cloud Functions so it would presumably use the metadata service to pick up the App Engine default service account (I think?).我还尝试在 Cloud Functions 中运行它,因此它可能会使用元数据服务来获取 App Engine 默认服务帐户(我认为?)。

In both cases, I am able to generate a token that appears valid.在这两种情况下,我都能够生成一个看起来有效的令牌。 Using jwt.io, I see that it contains the expected data and the signature is valid.使用 jwt.io,我看到它包含预期的数据并且签名有效。 However, when I make a request to the app using the token, I always get this exception:但是,当我使用令牌向应用程序发出请求时,我总是会收到以下异常:

Bad response from application: 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";应用程序的错误响应:401 / {'X-Goog-IAP-Generated-Response':'true','Date':'Tue,2021 年 2 月 9 日 19:25:43 GMT','Content-Type':'text /html', '服务器': '谷歌前端', '内容长度': '47', 'Alt-Svc': 'h3-29=":443"; ma=2592000,h3-T051=":443"; ma=2592000,h3-T051=":443"; ma=2592000,h3-Q050=":443"; ma=2592000,h3-Q050=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000,quic=":443"; ma=2592000;马=2592000; v="46,43"'} / 'Invalid GCIP ID token: JWT signature is invalid' v="46,43"'} / '无效的 GCIP ID 令牌:JWT 签名无效'

What could I be doing wrong?我可能做错了什么?

The solution to this problem is to exchange the Google Identity Token for an Identity Platform Identity Token.此问题的解决方案是将 Google 身份令牌交换为 Identity Platform 身份令牌。

The reason for the error Invalid GCIP ID token: JWT signature is invalid is caused by using a Google Identity Token which is signed by a Google RSA private key and not by a Google Identity Platform RSA private key.错误的原因Invalid GCIP ID token: JWT signature is invalid是由使用由 Google RSA 私钥而非 Google Identity Platform RSA 私钥签名的 Google Identity Token 引起的。 I overlooked GCIP in the error message, which would have told me the solution once we validated that the token was not corrupted in use.我在错误消息中忽略了GCIP ,一旦我们验证令牌在使用中没有损坏,它就会告诉我解决方案。

In the question, this line of code fetches the Google Identity Token:在问题中,这行代码获取 Google 身份令牌:

open_id_connect_token = id_token.fetch_id_token(Request(), client_id)

The above line of code requires that Google Cloud Application Default Credentials are setup.上述代码行要求设置 Google Cloud 应用程序默认凭据。 Example: set GOOGLE_APPLICATION_CREDENTIALS=c:\config\service-account.json示例: set GOOGLE_APPLICATION_CREDENTIALS=c:\config\service-account.json

The next step is to exchange this token for an Identity Platform token:下一步是将此令牌交换为 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)

The API Key can be found in the Google Cloud Console -> Identity Platform. API 密钥可以在 Google Cloud Console -> Identity Platform 中找到。 Top right "Application Setup Details".右上角的“应用程序设置详细信息”。 This will show the apiKey and authDomain .这将显示apiKeyauthDomain

More information can be found at this link:更多信息可以在这个链接中找到:

Exchanging a Google token for an Identity Platform token 用 Google 令牌交换 Identity Platform 令牌

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 无法使用 Python 中的服务帐户密钥文件发布请求,获取“无效的 IAP 凭据:无法解析 JWT”、“401 状态代码” - Cannot POST request using service account key file in Python, getting 'Invalid IAP credentials: Unable to parse JWT', '401 Status Code' Google Pub/Sub 将订阅推送到受 IAP 保护的 App Engine - Google Pub/Sub push subscription into IAP-protected App Engine Google Cloud Pub/Sub:推送订阅不调用受 IAP 保护的 GAE 应用程序 - Google Cloud Pub/Sub: push subscription doesn't call IAP-protected GAE app 来自服务帐户 OIDC 令牌的 IAP:401 Unauthorized - IAP from Service account OIDC Token : 401 Unauthorized id_token验证失败:无效的令牌签名:在Google云端点中 - id_token verification failed: Invalid token signature: in google cloud endpoints 应用引擎无效的连接ID - app engine invalid connection id 带有服务帐户但凭据无效的Google AppEngine - Google AppEngine with Service Account but invalid Credentials Google App Engine频道令牌无效 - Google App Engine channel token is invalid 从服务帐户进行身份验证。 Google App Engine 中的程序化 IAP 身份验证失败 - Authenticating from a service account. Programmatic IAP authentication fails in Google App Engine 将域添加到谷歌应用程序时无效的请求 - Invalid Request when Adding Domain to a google app
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM