4

我希望能够使用开发环境上的用户默认凭据以编程方式为 iap 生成一个 id 令牌(即我自己的笔记本电脑,安装并登录了 google cloud sdk)。

遵循文档时,我设法使用服务帐户文件生成了授权令牌。

在我的个人计算机上使用google.auth.default时,我可以看到类型的凭据google.oauth2.credentials.Credentials有一个refresh_token. 我想用它来生成令牌,因为它是在下面的文档中使用 curl 完成的, Authenticating from a desktop app -> Accessing the application但我无法让它工作。有人知道是否有办法以这种方式进行身份验证?

4

2 回答 2

8

正如 Matthew 所说,用于获取刷新令牌的客户端 ID 的项目应与 IAP 客户端 ID 的项目匹配。Gcloud 使用在path_to/google-cloud-sdk/lib/googlecloudsdk/api_lib/auth/util.py默认凭据 (DEFAULT_CREDENTIALS_DEFAULT_CLIENT_IDDEFAULT_CREDENTIALS_DEFAULT_CLIENT_SECRET) 中定义的客户端 ID 和密码。因此,您不能在google.auth.default()util.py更改的情况下使用刷新令牌,因为尝试获取 ID 令牌将失败:

{
 "error": "invalid_audience",
 "error_description": "The audience client and the client need to be in the same project."
}

您的选择是:

  1. 根据 Matthew 的回复/文档获取刷新令牌(缓存它以避免每次都需要用户授权)和 ID 令牌。
  2. 修补 gcloud util.py 中存在的客户端 ID 和密钥(可能会随着 gcloud 更新而更改)。

两个选项的示例代码:

import google.auth
import requests
import json

from webbrowser import open_new_tab
from time import sleep

# use gcloud app default credentials if gcloud's util.py is patched
def id_token_from_default_creds(audience): 
    cred, proj = google.auth.default()
    # data necessary for ID token
    client_id = cred.client_id
    client_secret= cred.client_secret
    refresh_token = str(cred.refresh_token)
    return id_token_from_refresh_token(client_id, client_secret, refresh_token, audience)

def id_token_from_refresh_token(client_id, client_secret, refresh_token, audience):
    oauth_token_base_URL = "https://www.googleapis.com/oauth2/v4/token"
    payload = {"client_id": client_id, "client_secret": client_secret,
                "refresh_token": refresh_token, "grant_type": "refresh_token",
                "audience": audience}
    res = requests.post(oauth_token_base_URL, data=payload)
    return (str(json.loads(res.text)[u"id_token"]))

# obtain ID token for provided Client ID: get authorization code -> exchange for refresh token -> obtain and return ID token
def id_token_from_client_id(client_id, client_secret, audience):
    auth_code = get_auth_code(client_id)
    refresh_token = get_refresh_token_from_code(auth_code, client_id, client_secret)
    return id_token_from_refresh_token(client_id, client_secret, refresh_token, audience)

def get_auth_code(client_id):
    auth_url = "https://accounts.google.com/o/oauth2/v2/auth?client_id=%s&response_type=code&scope=openid%%20email&access_type=offline&redirect_uri=urn:ietf:wg:oauth:2.0:oob"%client_id
    open_new_tab(auth_url)
    sleep(1)
    return raw_input("Authorization code: ")

def get_refresh_token_from_code(auth_code, client_id, client_secret):
    oauth_token_base_URL = 'https://www.googleapis.com/oauth2/v4/token'
    payload = {"code": auth_code, "client_id": client_id, "client_secret": client_secret,
                "redirect_uri": "urn:ietf:wg:oauth:2.0:oob", "grant_type": "authorization_code"}
    res = requests.post(oauth_token_base_URL, data=payload)
    return (str(json.loads(res.text)[u"refresh_token"]))

print("ID token from client ID: %s" % id_token_from_client_id("<Other client ID>", "<Other client secret>", "<IAP Client ID>")) # other client ID should be from the same project as IAP Client ID 
print("ID token from \"default\" credentials: %s" % id_token_from_default_creds("<IAP Client ID>"))
于 2018-03-06T10:51:58.137 回答
2

感谢您指出了这一点!如果我们有这方面的代码示例,我会很高兴,但正如您所发现的,至少对于 Python,我们为服务帐户身份验证提供的代码示例不适用于用户帐户。

我对我们的 Python 客户端库不够熟悉,无法告诉您它们是否可以帮助您解决这些问题,但https://cloud.google.com/iap/docs/authentication-howto#authenticating_from_a_desktop_app的要点是引导您完成的是:

  1. 为您的客户端应用创建一个新的客户端 ID(与受 IAP 保护的应用在同一项目中),并使用该客户端 ID 获取刷新令牌。您不能只使用应用程序默认凭据中的刷新令牌,因为这将具有错误的客户端 ID 和可能的范围。相反,您需要将“gcloud auth login”之类的功能添加到您的应用程序并保留刷新令牌。

  2. 获得刷新令牌后,当您的客户端应用程序想要访问 IAP 应用程序时:使用您应用程序的 OAuth 客户端的客户端 ID 和密钥(刷新令牌)发布到https://www.googleapis.com/oauth2/v4/token和 IAP 客户端 ID。这将返回一个 OpenID Connect 令牌,该令牌将有效地向 IAP 进行身份验证一小时。

这至少足以开始吗?

于 2018-03-06T00:50:07.853 回答