云 运行 - 通过服务帐户进行 gRPC 身份验证 - NodeJS

Cloud Run - gRPC authentication through service account - NodeJS

我必须向云 运行 服务发出经过身份验证的请求,该服务使用 HTTP/2 方法通过 grpc 进行通信。 服务 returns 一个文件的 signedURL 并且在未经身份验证的情况下部署时它会成功地执行相同的操作。 我在从客户端发出请求时将 JWT 令牌添加到元数据中。但是 grpc 服务器给出了以下错误 -

code: 16,
  metadata: Metadata {
    _internal_repr: {
      'www-authenticate': [Array],
      date: [Array],
      server: [Array],
      'x-envoy-upstream-service-time': [Array],
      'content-length': [Array]
    },
    flags: 0
Your client does not have permission to the requested URL

在云 运行 上,我在连接选项卡中启用了 HTTP/2。与此 Cloud 运行 服务关联的服务帐户也具有 Cloud 运行 Invoker 权限。 下面是实例化grpc server的代码-

const grpc = require('grpc')
const protoLoader = require('@grpc/proto-loader')
const packageDefinition = protoLoader.loadSync(
  `${__dirname}/${proto_file}`,
  {
    keepCase: true,
    longs: String,
    enums: String,
    defaults: true,
    oneofs: true
  })

const Proto = grpc.loadPackageDefinition(packageDefinition) 
function main () {
  const server = new grpc.Server()
  server.addService(Proto.${service_name}.service, { ${function_name} })
  server.bindAsync(`0.0.0.0:${PORT}`, grpc.ServerCredentials.createInsecure(), (error, port) => {
    if (error) {
      throw error
    }
    server.start()
    console.log('gRPC Server running!')
  })
}
main()

以下是发出经过身份验证的请求的客户端代码 -

const grpc = require('grpc')
const protoLoader = require('@grpc/proto-loader')
const {GoogleAuth} = require('google-auth-library');
const auth = new GoogleAuth();
const packageDefinition = protoLoader.loadSync(
    `${__dirname}/${proto_file}`, {
      keepCase: true,
      longs: String,
      enums: String,
      defaults: true,
      oneofs: true
    })
//* Or make the changes for authenticated call to CDN here
const Proto = grpc.loadPackageDefinition(packageDefinition)
async function request() {
  return auth.getIdTokenClient(targetAudience);
}

const metadata = new grpc.Metadata();

request().then((token)=> {
  metadata.add('Authorization', `Bearer ${token}`);
})

function getFileLink (projectId, fileId, plaintext = null) {

  const serverAddress = config.server_address
  let credentials
  if (plaintext) {
    credentials = grpc.credentials.createInsecure()
  } else {
    credentials = grpc.credentials.createSsl()
  } 
  const cdn = new cdnProto.CDN(serverAddress, grpc.credentials.createSsl())
  const request = {
    project_id: projectId,
    file_id: fileId
  }
  return new Promise((resolve, reject) => {
    cdn.GetFileLink(request, metadata, (error, response) => {
      if (error) {
        console.log(error)
        resolve(null)
      } else {
        resolve(response)
      }
    })
  })
}

我已经解决了这个问题。我的做法是对的,但是我获取token的方式不对。要获得所需的 auth Token,我还必须调用另一个方法。

async function request() {
  client = await auth.getIdTokenClient(targetAudience);
  return client.idTokenProvider.fetchIdToken(targetAudience)
}