如何使用用户默认凭据以编程方式对受 Cloud Identity-Aware Proxy (Cloud IAP) 保护的资源进行身份验证?
Posted
技术标签:
【中文标题】如何使用用户默认凭据以编程方式对受 Cloud Identity-Aware Proxy (Cloud IAP) 保护的资源进行身份验证?【英文标题】:How to authenticate programmatically to a Cloud Identity-Aware Proxy (Cloud IAP)-secured resource using user default credentials? 【发布时间】:2018-03-02 14:48:47 【问题描述】:我希望能够使用开发环境中的用户默认凭据以编程方式为 iap 生成一个 id 令牌(即我自己的笔记本电脑,安装并登录了 google cloud sdk)。
当关注documentation 时,我设法使用服务帐户文件生成了一个授权令牌。
在我的个人计算机上使用google.auth.default
时,我可以看到google.oauth2.credentials.Credentials
类型的凭据有一个refresh_token
。我想用它来生成令牌,因为它是在documentation 下使用 curl 完成的
Authenticating from a desktop app -> Accessing the application
但我无法让它工作。有人知道是否有办法以这种方式进行身份验证?
【问题讨论】:
你能详细说明为什么它不起作用吗?也许您看到了一些错误? 【参考方案1】:正如 Matthew 所说,用于获取刷新令牌的客户端 ID 的项目应与 IAP 客户端 ID 的项目相匹配。 Gcloud 使用在 path_to/google-cloud-sdk/lib/googlecloudsdk/api_lib/auth/util.py
中定义的客户端 ID 和密码作为默认凭据(DEFAULT_CREDENTIALS_DEFAULT_CLIENT_ID
和 DEFAULT_CREDENTIALS_DEFAULT_CLIENT_SECRET
)。因此,如果没有 util.py
更改,您不能使用来自 google.auth.default()
的刷新令牌,因为尝试获取 ID 令牌将失败:
"error": "invalid_audience",
"error_description": "The audience client and the client need to be in the same project."
您的选择是:
-
根据 Matthew 的回复/文档获取刷新令牌(缓存它以避免每次都需要用户授权)和 ID 令牌。
修补 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>"))
【讨论】:
感谢它从 ~/.config/gcloud/application_default_credentials.json 加载凭据的代码,您可以在您的环境中使用 GOOGLE_APPLICATION_CREDENTIALS 来指定一个新文件。我添加了另一个客户端凭据,但我有一个带有未经授权的客户端消息的 401。我目前正在研究它。 查看库,GCloud 凭据属于“authorized_user”类型,因此您确实可以尝试将 GOOGLE_APPLICATION_CREDENTIALS 环境变量设置为包含“refresh_token”的 json 文件的路径:“your_refresh_token”, “client_id”:“your_client_id”,“client_secret”:“your_client_secret”和“type”:“authorized_user”。我不明白你为什么要这样做,因为你首先需要获取刷新令牌才能填充文件,所以你基本上只需使用 google.auth 库来读取这个 json。【参考方案2】:感谢您指出这一点!如果我们有这方面的代码示例,我会很高兴,但正如您所发现的,至少对于 Python,我们为服务帐户身份验证提供的代码示例不适用于用户帐户。
我对我们的 Python 客户端库不够熟悉,无法告诉您它们是否可以帮助您解决这些问题,但 https://cloud.google.com/iap/docs/authentication-howto#authenticating_from_a_desktop_app 指导您完成的要点是:
为您的客户端应用创建一个新的客户端 ID(在与受 IAP 保护的应用相同的项目中),并使用该客户端 ID 获取刷新令牌。您不能只使用应用程序默认凭据中的刷新令牌,因为这将具有错误的客户端 ID 和可能的范围。相反,您需要将“gcloud auth login”等功能添加到您的应用程序中并保留刷新令牌。
拥有刷新令牌后,当您的客户端应用程序想要访问 IAP 应用程序时:使用您的应用程序 OAuth 客户端的客户端 ID 和密码、刷新令牌和 IAP 客户端 ID 发送到 https://www.googleapis.com/oauth2/v4/token .这将返回一个 OpenID Connect 令牌,该令牌将在 1 小时内有效地向 IAP 进行身份验证。
这至少足以开始了吗?
【讨论】:
非常感谢!我不确定我是否理解为什么用户而不是用户需要客户端 ID,因为他们都具有 IAP-Secured Web App User 角色? 查看它的一种方式是客户端应用程序的客户端 ID 告诉身份验证系统哪个应用程序正在对用户进行身份验证,而 IAP 应用程序的客户端 ID 指示用户想要访问哪个应用程序。在“用户身份验证”情况下需要客户端应用程序客户端 ID,但在“服务身份验证”情况下不需要,因为在服务帐户流程中,应用程序使用非 OAuth 机制(例如拥有私钥或 GCP平台魔术)来证明其充当服务帐户的权限。 好吧,我理解得更好了,如果有一种方法可以使用“GCP 平台魔法”,那么 gcloud 命令经过身份验证且有权访问 IAP 的用户可以以编程方式连接而无需再次登录。 我大致了解需要另一个客户端 ID。我不明白的是:我们需要在多大程度上保护这个二级客户的身份和秘密?这应该被视为敏感数据吗?似乎不太可能,否则数据永远无法打包到桌面样式的客户端中。硬编码这些凭据并提交版本控制是否安全? @Matthew,您对此有何看法?以上是关于如何使用用户默认凭据以编程方式对受 Cloud Identity-Aware Proxy (Cloud IAP) 保护的资源进行身份验证?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Monotouch 在 iPad 故事板应用程序中以编程方式管理视图
如何使用PowerShell中的当前Windows用户的凭据加密KeePass?