Google 云 (firebase) 可调用函数 401:"unauthorized" 由 http 调用时出错

Google cloud (firebase) callable function 401: "unauthorized" error when called by http

我通过 firebase deploy --only functions.

部署了简单的测试可调用函数

内容如下:

export const test = region('europe-west1').https.onCall((data, context) => {
    logger.debug('test call info', data, context);
    return 'hello, world!';
});

我可以通过对 https://europe-west1-{project}.cloudfunctions.net/test 进行 HTTP 调用并接收 {"result": "hello, world!"}.

来成功调用它

现在想通过 IAM 策略保护此功能。

  1. 我创建了新的服务帐户并加载了它的凭据json。
  2. 我转到控制台并从 allUsers 中删除 Cloud Functions Invoker 角色,并添加在 #1 中创建的具有 Cloud Functions Invoker 角色的服务帐户。 <--- 现在,当我尝试调用我的测试函数时,我收到 403 Forbidden,正如预期的那样
  3. 我在 PC 上使用我的服务帐户创建 id-token
const auth = new GoogleAuth({keyFile: './key.json'});
targetAudience = new URL('https://europe-west1-{project}.cloudfunctions.net/test');
const client = await auth.getIdTokenClient(targetAudience as string);
const idToken = await client.idTokenProvider.fetchIdToken(targetAudience as string);
console.log(idToken);
  1. 我确认我的令牌是正确的,并且是通过 https://www.googleapis.com/oauth2/v3/tokeninfo?id_token={{id-token}} 为正确的服务帐户颁发的。
  2. 我用 Authorization: Bearer {{id-token}} header.
  3. 调用我的端点
  4. 我收到 401 未授权错误 JSON 告诉我
{
    "error": {
        "message": "Unauthenticated",
        "status": "UNAUTHENTICATED"
    }
}
  1. 我很痛苦,因为在阅读了大部分 google 云文档、SO (, ) 等之后,似乎我做的一切都正确,但仍然出现此错误。

UPD1:向@DazWilkin 提供额外信息

1.

gcloud auth activate-service-account cloud-functions-invoker@{project-id}.iam.gserviceaccount.com --key-file="key.json" 

Activated service account credentials for: [cloud-functions-invoker@{project-id}.iam.gserviceaccount.com]
gcloud projects get-iam-policy {project-id}

...
- members:
  - serviceAccount:cloud-functions-admin@{project-id}.iam.gserviceaccount.com
  - serviceAccount:cloud-functions-invoker@{project-id}.iam.gserviceaccount.com
  role: roles/cloudfunctions.invoker
...
  1. 我复制这个值gcloud auth print-identity-token | clip
  2. 这是我调用 https://www.googleapis.com/oauth2/v3/tokeninfo?id_token={{id-token}}
  3. 后得到的结果
{
    "aud": "32555940559.apps.googleusercontent.com",
    "azp": "cloud-functions-invoker@{project-id}.iam.gserviceaccount.com",
    "email": "cloud-functions-invoker@{project-id}.iam.gserviceaccount.com",
    "email_verified": "true",
    "exp": "1629157648",
    "iat": "1629154048",
    "iss": "https://accounts.google.com",
    "sub": "114693730231812960252",
    "alg": "RS256",
    "kid": "6ef4bd908591f697a8a9b893b03e6a77eb04e51f",
    "typ": "JWT"
}
  1. 我做了这个卷曲(从邮递员那里复制的)。我也尝试过与 command-line 相同的卷曲 - 得到相同的结果。
curl --location --request POST 'https://europe-west1-{project-id}.cloudfunctions.net/test' \
--header 'Authorization: Bearer 
{token}
' \
--header 'Content-Type: application/json' \
--data-raw '{
    "data": {
        "goodbye": "world:("
    }
}'

UPD2:新输入

此行为仅在 可调用 函数 https.onCall() 中观察到。如果我使用 https.onRequest() 一切正常,但我仍然对 onCall 行为感到困惑,因为 似乎 我正确地实现了 protocol(请参阅我的卷曲)。

我的天啊!!!!!

TL;DR:可调用函数仅适用于使用 firebase auth 进行身份验证的最终用户,不能与 IAM 令牌一起使用。使用它们的唯一方法是将 roles/cloudfunctions.invoker 分配给 allUsers.


问题是...我正在研究 callable 函数协议规范:https://firebase.google.com/docs/functions/callable-reference.

它说:

The HTTP request to a callable trigger endpoint must be a POST with the following headers:

...

  • Optional: Authorization: Bearer
    • A Firebase Authentication user ID token for the logged-in user making the request. The backend automatically verifies this token and makes it available in the handler's context. If the token is not valid, the request is rejected.

我在想规范意味着我们应该在这里传递我们的 服务帐户 id-token,我正试图这样做(例如使用 onRequest 函数)。但是突然之间,token 在那里的解释不同。它是通过 firebase 身份验证获得的用户令牌。甚至在文档中更深入地说明了这一点 (1, 2):

Warning: The ID token verification methods included in the Firebase Admin SDKs are meant to verify ID tokens that come from the client SDKs, not the custom tokens that you create with the Admin SDKs. See Auth tokens for more information.

错过这个警告让我痛苦了 3 天。