如何使用python以编程方式获取GCP Bearer令牌

时间:2018-11-25 22:00:25

标签: python google-cloud-platform google-iam

gcloud auth print-access-token给了我一个Bearer令牌,以后可以使用;但是,这是一个shell命令。如何通过Google Cloud Python API以编程方式获取一个?

我看到使用prior exampleoauth2client,但现在已弃用oauth2client。我该如何使用google.authoauthlib来做到这一点?

7 个答案:

答案 0 :(得分:7)

答案取决于您的环境以及您如何创建/获取凭据。

什么是Google Cloud凭据?

Google Cloud凭据是OAuth 2.0令牌。此令牌至少具有Access Token,并可选地具有Refresh TokenClient ID Token和支持参数,例如expirationService Account EmailClient Email,等

Google Cloud API中的重要项目是Access Token。该令牌授权访问云。此令牌可用于诸如curl之类的程序,诸如python之类的软件等中,并且不需要SDK。 Access Token用于HTTP Authorization标头。

什么是访问令牌?

访问令牌是Google生成的不透明值,它是从Signed JWT(更正确地称为JWS)派生而来的。 JWT由标头和声明(有效负载)Json结构组成。这两个Json结构是使用服务帐户的私钥签名的。这些值经过base64编码并连接起来以创建访问密钥。

访问令牌的格式为:base64(header) + '.' + base64(payload) + '.' + base64(signature)

这是一个JWT示例:

标题:

{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "42ba1e234ac91ffca687a5b5b3d0ca2d7ce0fc0a"
}

有效载荷:

{
  "iss": "myservice@myproject.iam.gserviceaccount.com",
  "iat": 1493833746,
  "aud": "myservice.appspot.com",
  "exp": 1493837346,
  "sub": "myservice@myproject.iam.gserviceaccount.com"
}

使用访问令牌:

将启动虚拟机实例的示例。替换PROJECT_ID,ZONE和INSTANCE_NAME。此示例适用于Windows。

curl -v -X GET -H "Authorization: Bearer <access_token_here>" ^
https://www.googleapis.com/compute/v1/projects/%PROJECT_ID%/zones/%ZONE%/instances/%INSTANCE_NAME%/start

Compute Engine服务帐户:

在这种情况下,达斯汀的答案是正确的,但为了完整起见,我将包括一些其他信息。

这些凭据由GCP自动为您创建,并从VM实例元数据中获取。权限由Google控制台中的Cloud API access scopes控制。

但是,这些凭据有一些限制。要修改凭据,必须首先停止VM实例。此外,并非所有权限(角色)都受支持。

from google.auth import compute_engine

cred = compute_engine.Credentials()

服务帐户凭据:

直到您了解所有凭证类型及其用例,这些凭证将用于gcloudgsutil以外的所有凭证。了解这些凭据将使编写程序时使用Google Cloud变得更加简单。从Google服务帐户Json文件中获取凭证很容易。唯一需要注意的是凭据过期(通常为60分钟),并且需要刷新或重新创建。

不建议使用

gcloud auth print-access-token。服务帐户凭据是Google推荐的方法。

这些凭据是由控制台,gcloud或通过程序/ API创建的。权限由IAM分配给信用凭证,并在Compute Engine,App Engine,Firestore,Kubernetes等以及Google Cloud之外的其他环境中起作用。这些凭证是从Google Cloud下载的,并存储在Json文件中。注意scopes参数。这定义了授予结果凭证对象的权限。

SCOPES = ['https://www.googleapis.com/auth/sqlservice.admin']
SERVICE_ACCOUNT_FILE = 'service-account-credentials.json'

from google.oauth2 import service_account

cred = service_account.Credentials.from_service_account_file(
            SERVICE_ACCOUNT_FILE, scopes=SCOPES)

Google OAuth 2.0凭据:

这些凭据来自完整的OAuth 2.0流。这些凭据是在启动浏览器以访问Google帐户以授权访问时生成的。此过程要复杂得多,需要大量代码来实现,并且需要内置的Web服务器来进行授权回调。

此方法提供了其他功能,例如能够在浏览器中运行所有内容,例如,您可以创建云存储文件浏览器,但请注意了解安全隐患。此方法是用于支持Google登录等的技术。我喜欢使用此方法在允许用户在网站上发布信息之前对用户进行身份验证。使用正确授权的OAuth 2.0身份和范围的可能性是无限的。

使用google_auth_oauthlib的示例代码:

from google_auth_oauthlib.flow import InstalledAppFlow

flow = InstalledAppFlow.from_client_secrets_file(
    'client_secrets.json',
    scopes=scope)

cred = flow.run_local_server(
    host='localhost',
    port=8088,
    authorization_prompt_message='Please visit this URL: {url}',
    success_message='The auth flow is complete; you may close this window.',
    open_browser=True)

使用requests_oauthlib库的示例代码:

from requests_oauthlib import OAuth2Session

gcp = OAuth2Session(
        app.config['gcp_client_id'],
        scope=scope,
        redirect_uri=redirect_uri)

# print('Requesting authorization url:', authorization_base_url)

authorization_url, state = gcp.authorization_url(
                        authorization_base_url,
                        access_type="offline",
                        prompt="consent",
                        include_granted_scopes='true')

session['oauth_state'] = state

return redirect(authorization_url)


# Next section of code after the browser approves the request

token = gcp.fetch_token(
            token_url,
            client_secret=app.config['gcp_client_secret'],
            authorization_response=request.url)

答案 1 :(得分:3)

import google.auth
import google.auth.transport.requests


# getting the credentials and project details for gcp project
credentials, your_project_id = google.auth.default(scopes=["https://www.googleapis.com/auth/cloud-platform"])

#getting request object
auth_req = google.auth.transport.requests.Request()

print(credentials.valid) # prints False
credentials.refresh(auth_req) #refresh token
#cehck for valid credentials
print(credentials.valid)  # prints True
print(credentials.token) # prints token

答案 2 :(得分:1)

在某些情况下,在需要不记名访问令牌来调用 Google 云 API 时,无法在服务器或容器上设置环境变量。我提出以下解决此类问题:

# pip3 install google-auth
# pip3 install requests

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

credentials = service_account.Credentials.from_service_account_file('/home/user/secrets/hil-test.json', scopes=['https://www.googleapis.com/auth/cloud-platform'])
auth_req = google.auth.transport.requests.Request()
credentials.refresh(auth_req)
credentials.token

最后一行将打印用于调用 Google 云 API 的访问令牌。将以下 curl 命令中的 ya29<REDACTED> 替换为来自 python 的打印令牌作为测试:

curl https://example.googleapis.com/v1alpha1/projects/PROJECT_ID/locations -H "Authorization: Bearer ya29<REDACTED>"

执行 python 来获取令牌然后在 BASH 中 curl 调用 API 可能没有意义。目的是演示获取令牌以调用 Google Cloud Alpha API,该 API 可能没有任何 Python 客户端库,但有 REST API。然后,开发人员可以使用 Python requests HTTP 库来调用 API。

答案 3 :(得分:0)

虽然以上答案很有用,但它遗漏了一个要点-从google.auth.default()compute_engine.Credentials()获得的凭证对象中将没有令牌。回到最初的问题,gcloud auth print-access-token的替代方案是什么,我的答案是:

import google.auth
import google.auth.transport.requests
creds, projects = google.auth.default()

# creds.valid is False, and creds.token is None
# Need to refresh credentials to populate those

auth_req = google.auth.transport.requests.Request()
creds.refresh(auth_req)

# Now you can use creds.token

我正在使用官方的google-auth软件包和default credentials,这将使您既可以使用本地开发工具,也可以使用远程GCE / GKE应用程序。

很遗憾,这没有正确记录,我不得不阅读google-auth code来弄清楚我们如何获得令牌。

答案 4 :(得分:0)

我在这里寻找自己的方法而无需使用服务帐户即可使用python SDK。我想要一种本地开发可在云中运行的脚本的方法。我可以通过使用gcloud命令的工件来实现此目的:

export GOOGLE_APPLICATION_CREDENTIALS=~/.config/gcloud/legacy_credentials/<me>/adc.json

答案 5 :(得分:0)

这可能不是推荐的方法,但是对于我的应用程序中的Rest API,这是一种获取令牌的简便方法。

from subprocess import PIPE, Popen


def cmdline(command):
    process = Popen(
        args=command,
        stdout=PIPE,
        shell=True
    )
    return process.communicate()[0]


token = cmdline("gcloud auth application-default print-access-token")
print("Token:"+token)

答案 6 :(得分:0)

我结合了这篇文章和google cloud文档中的建议,编写了一个辅助函数来返回令牌。如果可能,它会生成一个令牌,如果不从环境中获取它,则将检查它是否有效。

import google
import os
import requests

GOOGLE_APPLICATION_CREDENTIALS = "GOOGLE_APPLICATION_CREDENTIALS"
GCS_OAUTH_TOKEN = "GCS_OAUTH_TOKEN"
SCOPE = "https://www.googleapis.com/auth/cloud-platform"
URL = "https://www.googleapis.com/oauth2/v1/tokeninfo"
PAYLOAD = "access_token={}"
HEADERS = {"content-type": "application/x-www-form-urlencoded"}
OK = "OK"


def get_gcs_token():
    """
    Returns gcs access token.
    Ideally, this function generates a new token, requries that GOOGLE_APPLICATION_CREDENTIALS be set in the environment
    (os.environ).
    Alternatively, environment variable GCS_OAUTH_TOKEN could be set if a token already exists
    """
    if GOOGLE_APPLICATION_CREDENTIALS in os.environ:
        # getting the credentials and project details for gcp project
        credentials, your_project_id = google.auth.default(scopes=[SCOPE])

        # getting request object
        auth_req = google.auth.transport.requests.Request()
        credentials.refresh(auth_req)  # refresh token
        token = credentials.token
    elif GCS_OAUTH_TOKEN in os.environ:
        token = os.environ[GCS_OAUTH_TOKEN]
    else:
        raise ValueError(
            f"""Could not generate gcs token because {GOOGLE_APPLICATION_CREDENTIALS} is not set in the environment.
Alternatively, environment variable {GCS_OAUTH_TOKEN} could be set if a token already exists, but it was not"""
        )

    r = requests.post(URL, data=PAYLOAD.format(token), headers=HEADERS)
    if not r.reason == OK:
        raise ValueError(
            f"Could not verify token {token}\n\nResponse from server:\n{r.text}"
        )
    if not r.json()["expires_in"] > 0:
        raise ValueError(f"token {token} expired")
    return token