如何在 Twilio 无服务器功能中为 google API 服务帐户使用 googleapis google.auth.GoogleAuth()?

How to use googleapis google.auth.GoogleAuth() for google API service account in Twilio serverless function?

如何在 Twilio 无服务器功能中为 google API 服务帐户使用 googleapis google.auth.GoogleAuth(),因为没有 FS 路径可作为 keyFile值?

基于此处的示例 (https://www.section.io/engineering-education/google-sheets-api-in-nodejs/ ) and here ( Google api node.js client documentation ) my code is based on the example here ( Receive an inbound SMS),看起来像...

const {google} = require('googleapis')
const fs = require('fs')
exports.handler = async function(context, event, callback) {
  const twiml = new Twilio.twiml.MessagingResponse()
  
  // console.log(Runtime.getAssets()["/gservicecreds.private.json"].path)
  console.log('Opening google API creds for examination...')
  const creds = JSON.parse(
    fs.readFileSync(Runtime.getAssets()["/gservicecreds.private.json"].path, "utf8")
  )
  console.log(creds)
  
  // connect to google sheet
  console.log("Getting googleapis connection...")
  const auth = new google.auth.GoogleAuth({
    keyFile: Runtime.getAssets()["/gservicecreds.private.json"].path,
    scopes: "https://www.googleapis.com/auth/spreadsheets",
  })
  const authClientObj = await auth.getClient()
  const sheets = google.sheets({version: 'v4', auth: authClientObj})
  const spreadsheetId = "myspreadsheetID"
  
  console.log("Processing message...")
  if (String(event.Body).trim().toLowerCase() == 'KEYWORD') {
    console.log('DO SOMETHING...')
    try {
      // see https://developers.google.com/sheets/api/guides/values#reading_a_single_range
      let response = await sheets.spreadsheets.values.get({
        spreadsheetId: spreadsheetId,
        range: "'My Sheet'!B2B1000"
      })
      console.log("Got data...")
      console.log(response)
      console.log(response.result)
      console.log(response.result.values)
    } catch (error) {
      console.log('An error occurred...')
      console.log(error)
      console.log(error.response)
      console.log(error.errors)
    }
  } 
  
  // Return the TwiML as the second argument to `callback`
  // This will render the response as XML in reply to the webhook request
  return callback(null, twiml)

...其中代码中引用的资产是通过为 Google APIs Service Account 创建密钥对并手动 copy/pasting JSON 数据生成的 JSON作为无服务器函数编辑器 Web 中的资产 UI.

我看到错误消息,例如...

An error occurred...

{ response: '[Object]', config: '[Object]', code: 403, errors: '[Object]' }

{ config: '[Object]', data: '[Object]', headers: '[Object]', status: 403, statusText: 'Forbidden', request: '[Object]' }

[ { message: 'The caller does not have permission', domain: 'global', reason: 'forbidden' } ]

假设这是由于在 auth const 声明中没有正确读取 keyFile(IDK 如何做到这一点,因为我的所有示例请参阅假设本地文件路径作为值,但 IDK 如何让函数访问该文件以实现无服务器函数(我在代码块中的尝试实际上只是在黑暗中拍摄)。

仅供参考,我可以看到该服务帐户在 google API 控制台中具有编辑者角色(尽管我注意到“此服务帐户可以访问的资源”有错误

"Could not fund an ancestor of the selected project where you have access to view a policy report on at least one ancestor"

(我真的不知道那是什么意思或暗示,对此很陌生))。例如...

任何人都可以帮助解决这里可能出现的问题吗?

(顺便说一句,如果我确实缺少某些东西 dumb/obvious(例如打字错误),只需在评论中 LMK 即可删除此 post(因为它不会为任何未来服务他人的价值))

The caller does not have permission', domain: 'global', reason: 'forbidden

这实际上意味着当前经过身份验证的用户(服务帐户)无权执行您要求它执行的操作。

您正在尝试访问点差 sheet。

这是 sheet 在服务帐户 google 驱动器帐户上吗?如果没有,您是否与服务帐户共享 sheet?

服务帐户与任何其他用户一样,如果它无法访问它无法访问的内容。转到 google 驱动器 Web 应用程序并与服务帐户共享 sheet,就像您与任何其他用户共享它一样,只需使用服务帐户电子邮件地址,我认为它称为客户端 ID,它带有@在里面。

委托给您域中的用户

如果您正确设置了委派,那么您可以让服务帐户充当您域中有权访问该文件的用户。

delegated_credentials = credentials.with_subject('userWithAccess@YourDomain.org')