繁体   English   中英

如何使用服务帐户密钥文件调用受限的 firebase function?

[英]How to call a restricted firebase function with a service account key file?

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

这是我所做并尝试过的:

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

  • 我从 GCP 上的 helloWorld 权限中删除了 allUsers 并验证了 function 不能再从客户端应用程序调用(我在响应中得到“403 Forbidden”)。

  • 我创建了一个新的服务帐户,并将其添加为 GCP 上 helloWorld 权限面板中的“Cloud functions invoker”的成员。

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

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

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”和以下 header:

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

之后,我通过以下教程尝试了另一种方法(请参见下面的代码)。

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 function。

但是,我定位的客户端应用程序不能依赖 gcloud cli,我一直坚持尝试了解上面第一个版本中哪些内容不起作用以及我需要更改哪些内容才能使其正常工作。

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

package.json

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

索引.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)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM