【问题标题】:How to call a restricted firebase function with a service account key file?如何使用服务帐户密钥文件调用受限的 firebase 函数?
【发布时间】:2020-10-28 14:48:13
【问题描述】:

我正在尝试设置一个受限制的 firebase 函数,该函数可以从在 GCP 外部运行的另一个客户端应用程序调用。到目前为止,我未能设置客户端应用程序身份验证以通过 firebase 功能上的受限访问。

这是我所做和尝试的:

  • 我创建并部署了一个简单的 helloWorld firebase 函数,并验证该函数可以从具有默认公共访问权限的客户端应用程序调用。

  • 我从 GCP 的 helloWorld 权限中删除了 allUsers,并验证无法再从客户端应用程序调用该函数(我在响应中收到“403 Forbidden”)。

  • 我创建了一个新的服务帐户,并将其添加为 GCP 上 helloWorld 权限面板中“云函数调用者”的成员。

  • 我为此服务帐户创建了一个新的私有 json 密钥文件。

然后我按照documentation 设置客户端应用程序身份验证(参见下面的代码)。

const fetch = require('node-fetch');
const jwt = require('jsonwebtoken');

async function main(){
  // get unix timestamp in seconds
  const current_time = Math.floor(Date.now() / 1000)

  // get the service account key file
  const service_account = require('./service_account.json');

  // create the jwt body
  const token_body = {
    "iss": service_account.client_email,
    "scope": "https://www.googleapis.com/auth/cloud-platform",
    "aud": "https://oauth2.googleapis.com/token",
    "exp": current_time + 3600,
    "iat": current_time
  }

  // sign the token with the private key
  const signed_token = jwt.sign(
    token_body, service_account.private_key, { algorithm: 'RS256' }
  )

  // get an access token from the authentication server
  const access_token = await fetch(
    'https://oauth2.googleapis.com/token',
    {
      method: 'POST',
      body: ''
      + 'grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer'
      + '&'
      + 'assertion=' + signed_token,
      headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
    }
  ).then(res => res.json()).then(body => body.access_token)

  // call the firebase function with the Authorization header
  return fetch(
    url_hello_world, { headers: { 'Authorization': 'Bearer ' + access_token } }
  ).then(res => res.text()).then(console.log)
}

main().catch(console.error)

不幸的是,当我运行之前的代码时,我得到“401 Unauthorize”并带有以下标题:

www-authenticate: Bearer error="invalid_token" error_description="The access token could not be verified"

之后我尝试了另一种方法,使用以下tutorial(请参见下面的代码)。

const fetch = require('node-fetch');
const util = require('util');
const exec = util.promisify(require("child_process").exec)

async function main(){
  // activate a service account with a key file
  await exec('gcloud auth activate-service-account --key-file=' + key_file)

  // retrieve an access token for the activated service account
  const {stdout, stderr} = await exec("gcloud auth print-identity-token")

  // get the access token from stdout and remove the new line character at the
  // end of the string
  const access_token = stdout.slice(0,-1)

  // call the firebase function with the Authorization header
  const response = await fetch(
    url_hello_world,
    { headers: { 'Authorization': 'Bearer ' + access_token } }
  )

  // print the response
  console.log(await response.text())
}

main().catch(console.error)

当我运行此代码时,我得到了预期的响应“Hello World”,因此前面的代码可以使用服务帐户权限调用 firebase 函数。

但是,我所针对的客户端应用程序不能依赖 gcloud cli,我被困在我试图了解上述第一个版本中什么不起作用以及我需要进行哪些更改以使其起作用的地步。

【问题讨论】:

    标签: node.js firebase google-cloud-platform


    【解决方案1】:

    当您使用服务帐户JSON 文件时,以下代码可能会有所帮助。

    package.json

    {
        "name": "sample-call",
        "version": "0.0.1",
        "dependencies": {
            "googleapis": "^62.0.0",
            "node-fetch": "^2.6.1"
        }
    }
    

    index.js

    var { google } = require('googleapis')
    const fetch = require('node-fetch')
    const fs = require('fs');
    let privatekey = JSON.parse(fs.readFileSync('key.json'));
    let url_hello_world = 'CLOUD_FUNCTION_URL';
    
    let jwtClient = new google.auth.JWT(
        privatekey.client_email,
        null,
        privatekey.private_key,
        url_hello_world
    )
    
    async function main(){
        jwtClient.authorize( async function(err, _token) {
            if (err) {
                console.log(err)
                return err
            } else {
                const response = await fetch(
                    url_hello_world,
                    {
                        headers: { 'Authorization': 'Bearer ' + _token.id_token }
                    }
                )
                console.log(await response.text())
            }
        })
    }
      
    main().catch(console.error)
    

    【讨论】:

    • 我尝试使用您提供的代码,但没有成功。我得到“401 未经授权”。
    • 不知道为什么,实际上我使用 Firebase CLI 和本机 GCP 函数部署了函数,并且在这两种情况下,身份验证都有效。还使用gcloud auth print-identity-tokengcloud auth print-access-token 进行测试。您能否在您的代码中使用这两个命令进行测试?
    猜你喜欢
    • 2020-11-30
    • 2018-06-21
    • 2014-09-07
    • 1970-01-01
    • 2016-06-26
    • 2017-09-29
    • 1970-01-01
    • 1970-01-01
    • 2018-05-30
    相关资源
    最近更新 更多