Python:如何使用特定用户而不是get_application_default()创建GoogleCredentials

Python:如何使用特定用户而不是get_application_default()创建GoogleCredentials,python,gcloud,google-cloud-python,gcloud-python,Python,Gcloud,Google Cloud Python,Gcloud Python,我正在更新一个脚本来调用OAuth2保护的Google云端点。以前的版本假定一个用户以前通过gcloud auth login进行过身份验证,因此可以使用默认值: credentials = GoogleCredentials.get_application_default() http = credentials.authorize(http) 但是,现在我必须以用户A和用户B的身份进行一些调用。我可以在shell中执行这些步骤来生成访问令牌,但我更愿意直接在程序中执行: gcloud au

我正在更新一个脚本来调用OAuth2保护的Google云端点。以前的版本假定一个用户以前通过gcloud auth login进行过身份验证,因此可以使用默认值:

credentials = GoogleCredentials.get_application_default()
http = credentials.authorize(http)
但是,现在我必须以用户A和用户B的身份进行一些调用。我可以在shell中执行这些步骤来生成访问令牌,但我更愿意直接在程序中执行:

gcloud auth login user_A@email.com
gcloud auth print-access-token user_A@email.com

有没有一种方法可以在不运行任何shell命令的情况下为两封不同的电子邮件生成两个凭据值?

您可能希望使用

gcloud auth application-default login
gcloud auth application-default print-access-token
而不是gcloud身份验证登录

但如果您使用的是gcloud凭据而不是应用程序默认值,请注意

gcloud auth login
是一个交互式命令。您可以选择用户在浏览器中而不是在命令行上登录

您可以预先登录,然后使用所需的凭据。例如:

gcloud auth login --account user_A@email.com
gcloud auth login --account user_B@email.com
这会将凭据添加到凭据存储注意,here-account仅用于验证,确保webflow选择的帐户与此处请求的帐户相同。您可以通过运行

gcloud auth list
然后,您可以根据需要使用特定帐户

gcloud auth print-access-token --account user_A@email.com
gcloud auth print-access-token --account user_B@email.com
请注意,打印访问令牌是未记录的命令,您应该仅将其用于调试

更高级的功能是使用配置

gcloud config configurations list
您可以通过

gcloud config configurations create A
gcloud config set account user_A@email.com
gcloud config set project project_A

gcloud config configurations create B
gcloud config set account user_B@email.com
gcloud config set project project_B
那你就可以了

gcloud auth print-access-token --configuration A
gcloud auth print-access-token --configuration B

附加的优点是,不仅可以配置帐户,还可以配置其他属性,如项目、计算区域等…

您可能想要使用的属性

gcloud auth application-default login
gcloud auth application-default print-access-token
而不是gcloud身份验证登录

但如果您使用的是gcloud凭据而不是应用程序默认值,请注意

gcloud auth login
是一个交互式命令。您可以选择用户在浏览器中而不是在命令行上登录

您可以预先登录,然后使用所需的凭据。例如:

gcloud auth login --account user_A@email.com
gcloud auth login --account user_B@email.com
这会将凭据添加到凭据存储注意,here-account仅用于验证,确保webflow选择的帐户与此处请求的帐户相同。您可以通过运行

gcloud auth list
然后,您可以根据需要使用特定帐户

gcloud auth print-access-token --account user_A@email.com
gcloud auth print-access-token --account user_B@email.com
请注意,打印访问令牌是未记录的命令,您应该仅将其用于调试

更高级的功能是使用配置

gcloud config configurations list
您可以通过

gcloud config configurations create A
gcloud config set account user_A@email.com
gcloud config set project project_A

gcloud config configurations create B
gcloud config set account user_B@email.com
gcloud config set project project_B
那你就可以了

gcloud auth print-access-token --configuration A
gcloud auth print-access-token --configuration B

另外一个优点是,您不仅可以配置account,还可以配置project、compute zone等其他属性。

我的案例有点不同,但希望您能找到与您的用例相同的属性。 我正在使用服务帐户进行身份验证

成功的身份验证需要3件事

服务帐户文件。这是创建服务帐户后获得的JSON文件

范围。这将指定您需要访问的Google API。在我的例子中,我需要访问Google Groups API。 因此,我需要的范围是:

您可以在此处找到所有作用域的列表:

所需用户的电子邮件地址。这是您的服务帐户要模拟的用户的电子邮件地址_A@email.com. 在我的情况下,我需要G套件管理员的电子邮件,因为他们只能访问谷歌群组。 以下是等效的Python代码:

from google.oauth2 import service_account
from googleapiclient.discovery import build

SCOPES = ["https://www.googleapis.com/auth/admin.directory.group"]
SERVICE_ACCOUNT_FILE = "my-service-account.json"

credentials = service_account.Credentials.from_service_account_file(
        SERVICE_ACCOUNT_FILE,
        scopes=SCOPES,
        subject="admin@example.com")

admin_service = build("admin", "directory_v1", credentials=credentials)
group = admin_service.groups().list(domain="example.com").execute()
print("groups list: ", group)

现在,根据您的问题,如果您想模拟其他用户。然后你可以用

new_user_email = "user2@example.com"
user2_credentials = credentials.with_subject(new_user_email)
new_service = build("admin", "directory_v1", credentials=user2_credentials)
# use this new_service to call required APIs
以下是使用python库服务帐户进行身份验证的文档链接:

下面是Oauth流一般如何在服务帐户中发生的链接:

我希望这对你有所帮助


PS-这是我关于堆栈溢出的第一个答案,请告诉我如何改进我的答案。

我的案例有点不同,但希望您能找到与您的用例相同的答案。 我正在使用服务帐户进行身份验证

成功的身份验证需要3件事

服务帐户文件。这是创建服务帐户后获得的JSON文件

范围。这将指定您需要访问的Google API。在我的例子中,我需要访问Google Groups API。 因此,我需要的范围是:

您可以在此处找到所有作用域的列表:

所需用户的电子邮件地址。这是您的服务帐户要模拟的用户的电子邮件地址_A@email.com. 在我的情况下,我需要G套件管理员的电子邮件,因为他们只能访问谷歌群组。 以下是等效的Python代码:

from google.oauth2 import service_account
from googleapiclient.discovery import build

SCOPES = ["https://www.googleapis.com/auth/admin.directory.group"]
SERVICE_ACCOUNT_FILE = "my-service-account.json"

credentials = service_account.Credentials.from_service_account_file(
        SERVICE_ACCOUNT_FILE,
        scopes=SCOPES,
        subject="admin@example.com")

admin_service = build("admin", "directory_v1", credentials=credentials)
group = admin_service.groups().list(domain="example.com").execute()
print("groups list: ", group)

现在,根据您的问题,如果您想模拟其他用户。然后你可以用

new_user_email = "user2@example.com"
user2_credentials = credentials.with_subject(new_user_email)
new_service = build("admin", "directory_v1", credentials=user2_credentials)
# use this new_service to call required APIs
以下是使用python库服务帐户进行身份验证的文档链接:

下面是Oauth流一般如何在服务帐户中发生的链接:

我希望这对你有所帮助


PS-这是我关于堆栈溢出的第一个答案,所以请告诉我如何改进我的答案。

据我所知,您希望提出请求 到OAuth2.0受保护的资源,并在python代码中指定调用方帐户

这是我个人使用的一个很好的函数,它比您需要的要多,因为它的工作方式与调用它的位置不同。您只需为它提供您要用于呼叫的帐户的客户端ID

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'] = 120

    # Figure out what environment we're running in and get some preliminary
    # information about the service account.
    bootstrap_credentials, _ = google.auth.default(
        scopes=[IAM_SCOPE])
    if isinstance(bootstrap_credentials,
                  google.oauth2.credentials.Credentials):
        raise Exception('make_iap_request is only supported for service '
                        'accounts.')
    elif isinstance(bootstrap_credentials,
                    google.auth.app_engine.Credentials):
        requests_toolbelt.adapters.appengine.monkeypatch()

    # For service account's using the Compute Engine metadata service,
    # service_account_email isn't available until refresh is called.
    bootstrap_credentials.refresh(Request())

    signer_email = bootstrap_credentials.service_account_email
    if isinstance(bootstrap_credentials,
                  google.auth.compute_engine.credentials.Credentials):
        # Since the Compute Engine metadata service doesn't expose the service
        # account key, we use the IAM signBlob API to sign instead.
        # In order for this to work:
        #
        # 1. Your VM needs the https://www.googleapis.com/auth/iam scope.
        #    You can specify this specific scope when creating a VM
        #    through the API or gcloud. When using Cloud Console,
        #    you'll need to specify the "full access to all Cloud APIs"
        #    scope. A VM's scopes can only be specified at creation time.
        #
        # 2. The VM's default service account needs the "Service Account Actor"
        #    role. This can be found under the "Project" category in Cloud
        #    Console, or roles/iam.serviceAccountActor in gcloud.
        signer = google.auth.iam.Signer(
            Request(), bootstrap_credentials, signer_email)
    else:
        # A Signer object can sign a JWT using the service account's key.
        signer = bootstrap_credentials.signer

    # Construct OAuth 2.0 service account credentials using the signer
    # and email acquired from the bootstrap credentials.
    service_account_credentials = google.oauth2.service_account.Credentials(
        signer, signer_email, token_uri=OAUTH_TOKEN_URI, additional_claims={
            'target_audience': client_id
        })

    # service_account_credentials gives us a JWT signed by the service
    # account. Next, we use that to obtain an OpenID Connect token,
    # which is a JWT signed by Google.
    google_open_id_connect_token = get_google_open_id_connect_token(
        service_account_credentials)

    # 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(
            google_open_id_connect_token)}, **kwargs)
    #time.sleep(50)
    if resp.status_code == 403:
        raise Exception('Service account {} does not have permission to '
                        'access the IAP-protected application.'.format(
                            signer_email))
    elif resp.status_code != 200:
        raise Exception(
            'Bad response from application: {!r} / {!r} / {!r}'.format(
                resp.status_code, resp.headers, resp.text))
    elif rest.status_code == 500:
        time.sleep(90)
        return 'DONE'
    else:
        return resp.text


def get_google_open_id_connect_token(service_account_credentials):
    """Get an OpenID Connect token issued by Google for the service account.

    This function:

      1. Generates a JWT signed with the service account's private key
         containing a special "target_audience" claim.

      2. Sends it to the OAUTH_TOKEN_URI endpoint. Because the JWT in #1
         has a target_audience claim, that endpoint will respond with
         an OpenID Connect token for the service account -- in other words,
         a JWT signed by *Google*. The aud claim in this JWT will be
         set to the value from the target_audience claim in #1.

    For more information, see
    https://developers.google.com/identity/protocols/OAuth2ServiceAccount .
    The HTTP/REST example on that page describes the JWT structure and
    demonstrates how to call the token endpoint. (The example on that page
    shows how to get an OAuth2 access token; this code is using a
    modified version of it to get an OpenID Connect token.)
    """

    service_account_jwt = (
        service_account_credentials._make_authorization_grant_assertion())
    request = google.auth.transport.requests.Request()
    body = {
        'assertion': service_account_jwt,
        'grant_type': google.oauth2._client._JWT_GRANT_TYPE,
    }
    token_response = google.oauth2._client._token_endpoint_request(
        request, OAUTH_TOKEN_URI, body)
    return token_response['id_token']

您可以通过其他方式阅读更多信息。

据我所知,您希望向OAuth2.0受保护的资源发出请求,并在python代码中指定调用方帐户

这是我个人使用的一个很好的函数,它比您需要的要多,因为它的工作方式与调用它的位置不同。您只需为它提供您要用于呼叫的帐户的客户端ID

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'] = 120

    # Figure out what environment we're running in and get some preliminary
    # information about the service account.
    bootstrap_credentials, _ = google.auth.default(
        scopes=[IAM_SCOPE])
    if isinstance(bootstrap_credentials,
                  google.oauth2.credentials.Credentials):
        raise Exception('make_iap_request is only supported for service '
                        'accounts.')
    elif isinstance(bootstrap_credentials,
                    google.auth.app_engine.Credentials):
        requests_toolbelt.adapters.appengine.monkeypatch()

    # For service account's using the Compute Engine metadata service,
    # service_account_email isn't available until refresh is called.
    bootstrap_credentials.refresh(Request())

    signer_email = bootstrap_credentials.service_account_email
    if isinstance(bootstrap_credentials,
                  google.auth.compute_engine.credentials.Credentials):
        # Since the Compute Engine metadata service doesn't expose the service
        # account key, we use the IAM signBlob API to sign instead.
        # In order for this to work:
        #
        # 1. Your VM needs the https://www.googleapis.com/auth/iam scope.
        #    You can specify this specific scope when creating a VM
        #    through the API or gcloud. When using Cloud Console,
        #    you'll need to specify the "full access to all Cloud APIs"
        #    scope. A VM's scopes can only be specified at creation time.
        #
        # 2. The VM's default service account needs the "Service Account Actor"
        #    role. This can be found under the "Project" category in Cloud
        #    Console, or roles/iam.serviceAccountActor in gcloud.
        signer = google.auth.iam.Signer(
            Request(), bootstrap_credentials, signer_email)
    else:
        # A Signer object can sign a JWT using the service account's key.
        signer = bootstrap_credentials.signer

    # Construct OAuth 2.0 service account credentials using the signer
    # and email acquired from the bootstrap credentials.
    service_account_credentials = google.oauth2.service_account.Credentials(
        signer, signer_email, token_uri=OAUTH_TOKEN_URI, additional_claims={
            'target_audience': client_id
        })

    # service_account_credentials gives us a JWT signed by the service
    # account. Next, we use that to obtain an OpenID Connect token,
    # which is a JWT signed by Google.
    google_open_id_connect_token = get_google_open_id_connect_token(
        service_account_credentials)

    # 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(
            google_open_id_connect_token)}, **kwargs)
    #time.sleep(50)
    if resp.status_code == 403:
        raise Exception('Service account {} does not have permission to '
                        'access the IAP-protected application.'.format(
                            signer_email))
    elif resp.status_code != 200:
        raise Exception(
            'Bad response from application: {!r} / {!r} / {!r}'.format(
                resp.status_code, resp.headers, resp.text))
    elif rest.status_code == 500:
        time.sleep(90)
        return 'DONE'
    else:
        return resp.text


def get_google_open_id_connect_token(service_account_credentials):
    """Get an OpenID Connect token issued by Google for the service account.

    This function:

      1. Generates a JWT signed with the service account's private key
         containing a special "target_audience" claim.

      2. Sends it to the OAUTH_TOKEN_URI endpoint. Because the JWT in #1
         has a target_audience claim, that endpoint will respond with
         an OpenID Connect token for the service account -- in other words,
         a JWT signed by *Google*. The aud claim in this JWT will be
         set to the value from the target_audience claim in #1.

    For more information, see
    https://developers.google.com/identity/protocols/OAuth2ServiceAccount .
    The HTTP/REST example on that page describes the JWT structure and
    demonstrates how to call the token endpoint. (The example on that page
    shows how to get an OAuth2 access token; this code is using a
    modified version of it to get an OpenID Connect token.)
    """

    service_account_jwt = (
        service_account_credentials._make_authorization_grant_assertion())
    request = google.auth.transport.requests.Request()
    body = {
        'assertion': service_account_jwt,
        'grant_type': google.oauth2._client._JWT_GRANT_TYPE,
    }
    token_response = google.oauth2._client._token_endpoint_request(
        request, OAUTH_TOKEN_URI, body)
    return token_response['id_token']

您可以通过其他方式阅读更多信息。

谢谢!不过,我正在寻找如何在python中实现这一点。谢谢!然而,我正在寻找如何在python中实现这一点。