在 python 云函数中验证 JWT 访问令牌时遇到问题

Posted

技术标签:

【中文标题】在 python 云函数中验证 JWT 访问令牌时遇到问题【英文标题】:Trouble validating JWT access token in python cloud function 【发布时间】:2019-09-10 02:44:32 【问题描述】:

我正在尝试向我的 python 云函数添加授权。我在 GCP 项目中创建了一个服务帐户并生成了密钥。调用 Cloud Function 的测试客户端代码(不在 GCP 中)如下所示:

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

SERVICE_ACCOUNT_FILE = '<my_project_key_file>.json'

credentials = service_account.Credentials.from_service_account_file(SERVICE_ACCOUNT_FILE, 
scopes=['https://www.googleapis.com/auth/userinfo.email'])
authed_session = AuthorizedSession(credentials)
response = authed_session.get('https://<my_project>.cloudfunctions.net/authValidation')

我知道此代码正确地从 Google 获取 JWT 不记名令牌,并在调用我的 Cloud Function 时添加到 Authorization 标头中。我只是很难在 Cloud Function 中验证该令牌。该代码的相关部分如下所示:

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

def hello_world(request):

    #  from https://developers.google.com/identity/sign-in/web/backend-auth#using-a-google-api-client-library    
    idinfo = id_token.verify_oauth2_token(request.headers.get('Authorization')[7:]), requests.Request())

我知道 id 令牌是正确的,因为手动验证(使用 https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=xxx )返回的正是我所期望的。

我得到的错误记录堆栈跟踪是:

Traceback (most recent call last):
  File "/env/local/lib/python3.7/site-packages/google/cloud/functions/worker.py", line 346, in run_http_function
    result = _function_handler.invoke_user_function(flask.request)
  File "/env/local/lib/python3.7/site-packages/google/cloud/functions/worker.py", line 217, in invoke_user_function
    return call_user_function(request_or_event)
  File "/env/local/lib/python3.7/site-packages/google/cloud/functions/worker.py", line 210, in call_user_function
    return self._user_function(request_or_event)
  File "/user_code/main.py", line 17, in hello_world
    idinfo = id_token.verify_oauth2_token(request.headers.get('Authorization')[7:], requests.Request())
  File "/env/local/lib/python3.7/site-packages/google/oauth2/id_token.py", line 141, in verify_oauth2_token
    certs_url=_GOOGLE_OAUTH2_CERTS_URL)
  File "/env/local/lib/python3.7/site-packages/google/oauth2/id_token.py", line 122, in verify_token
    return jwt.decode(id_token, certs=certs, audience=audience)
  File "/env/local/lib/python3.7/site-packages/google/auth/jwt.py", line 219, in decode
    header, payload, signed_section, signature = _unverified_decode(token)
  File "/env/local/lib/python3.7/site-packages/google/auth/jwt.py", line 139, in _unverified_decode
    header = _decode_jwt_segment(encoded_header)
  File "/env/local/lib/python3.7/site-packages/google/auth/jwt.py", line 112, in _decode_jwt_segment
    six.raise_from(new_exc, caught_exc)
  File "<string>", line 3, in raise_from
ValueError: Can't parse segment: b'\xc9\xad\xbd'

我在这里缺少什么?谢谢

【问题讨论】:

request.headers.get('Authorization')[7:] 的前 10 个字符是什么?问题看起来像您没有正确抓取令牌并且解码失败。 授权标头是Bearer ya29.c.EljxBqdHSeW1IQc.... 同样,如果我采用该值(减去“承载”指示符)并将其放入googleapis.com/oauth2/v1/tokeninfo?access_token=xxx 它会正确验证它,所以我不确定我是什么“离线”验证器代码丢失了。 你能解决这个问题吗?我也遇到了。 【参考方案1】:

确保传递给id_token.verify_oauth2_token() 的字符串的开头没有“Bearer”。

【讨论】:

【参考方案2】:

通过在本地系统中设置GOOGLE_APPLICATION_CREDENTIAL 环境变量,您的客户端将在该服务帐户的上下文中运行,而无需担心身份验证。您无需编写密钥文件的路径。

也适用于部署云功能,并在本地进行测试。当您部署云函数时,它会作为 AppEngine 默认服务帐户或您使用 --service-account 参数指定的服务帐户运行: https://cloud.google.com/sdk/gcloud/reference/functions/deploy

参考: https://cloud.google.com/docs/authentication/production

这样,您无需将密钥推送到服务器或在 git 中担心它,并且您也无需在本地和远程运行时进行任何代码更改。

【讨论】:

错过了这是一个测试客户端,虽然同样的原理也适用,但是看看这个测试客户端:cloud.google.com/functions/docs/bestpractices/… 这是用于存储触发的功能,但也有用于 pubsub 的示例。跨度> 我认为更重要的问题是为什么要使用云功能来摄取必须经过身份验证的流量?除了 CORS 之外,Google 没有提供任何有关为 http 功能验证客户端的文档,但它们提供了其他几种易于使用的工具来满足要求,例如 Pub/Sub 和 CloudEndpoints,其中 auth 就像 get_user 或配置一样简单。 我不同意,编程就是解决问题,通常有很多方法可以解决问题。在这种情况下,我提供了一个不同的解决方案,即 Cloud Endpoints 或 Pub/Sub,它们都可以解决手头的问题。现在,如果您想回答他关于如何在 http 函数中验证 access_token 的问题而不是批评我,请继续。

以上是关于在 python 云函数中验证 JWT 访问令牌时遇到问题的主要内容,如果未能解决你的问题,请参考以下文章

尝试使用有效令牌直接访问页面时,JWT 身份验证不起作用;将在登录时开始工作。反应/节点/快递

Firebase JWT 库无法验证 Python JWT 令牌

jwt 访问令牌和刷新令牌流

每个请求的 JWT 令牌验证?

如何使用 jwt 公钥在 Spring Boot 中验证承载访问令牌

如何基于使用 Oauth2 协议的身份验证改进 JWT 访问令牌和刷新令牌?