Google Cloud 函数使用服务帐户域范围访问调用 Gmail API

标签 go google-cloud-functions gmail-api service-accounts

// Package p contains an HTTP Cloud Function.
package p

import (
    "log"
    "fmt"
    "net/http"
    "context"
    "google.golang.org/api/gmail/v1"
)


func HelloWorld(wx http.ResponseWriter, rx *http.Request) {
    log.Println("start")

    srv, err := gmail.NewService(context.Background())
    if err != nil {
        log.Fatalf("Unable to retrieve Gmail client: %v", err)
    }

    user := "me"
    r, err := srv.Users.Labels.List(user).Do()
    if err != nil {
        log.Fatalf("Unable to retrieve labels: %v", err)
    }
    if len(r.Labels) == 0 {
        fmt.Println("No labels found.")
        return
    }
    fmt.Println("Labels:")
    for _, l := range r.Labels {
        fmt.Printf("- %s\n", l.Name)
    }

    log.Println("end")
}

我正在用 Go 编写一个 Google Cloud 函数,该函数需要访问拥有该项目、gmail 帐户并创建云函数的管理员用户 ID 的 Gmail。

使用页面Choose the best way to use and authenticate service accounts on Google Cloud上的信息,我确定应该使用“Attach Service Account”选项进行身份验证。

按照页面 Using OAuth 2.0 for Server to Server Applications 上的说明进行操作,我创建了一个新的服务帐户,并将域范围内的访问权限委托(delegate)给它 https://mail.google.com/ 我将新服务帐户分配为云功能的运行时服务帐户。

gmail.NewService 语句似乎执行成功,但 srv.Users.Labels.List(user) 语句失败并显示“错误 400:先决条件检查”。 日志文件如下。

2022-07-09T03:34:14.575564Z testgogmail hy9add8bqppl 2022/07/09 03:34:14 start

2022-07-09T03:34:14.785116Z testgogmail hy9add8bqppl 2022/07/09 03:34:14 Unable to retrieve labels: googleapi: Error 400: Precondition check failed., failedPrecondition 

2022-07-09T03:34:14.799538102Z testgogmail hy9add8bqppl Function execution took 553 ms. Finished with status: connection error

那我错过了什么?我做错了什么?

最佳答案

据我所知,当前,当您依赖基本的域范围访问/身份验证时,无法使用 Gmail API for Go 模拟另一个帐户。 gmail.NewService(context.Background()) 语句成功执行,但您已通过服务帐户的 gmail 地址进行身份验证。由于该帐户实际上并不存在,因此即使您传递不同/有效的电子邮件帐户作为用户参数,后续的 Users.Labels.List 也会失败。

但是,如果您使用 google.JWTConfigFromJSON 基于具有域范围访问权限的服务帐户创建身份验证 token ,然后在创建 Gmail 服务时使用 WithTokenSource 选项,则该方法确实有效。注意 - 下面的示例代码基于从在线 UI(而不是云 shell)创建和执行云函数。

    pathWD, err := os.Getwd()
    if err != nil {
        log.Println("error getting working directory:", err)
    }
    log.Println("pathWD: ", pathWD)

    jsonPath := pathWD + "/serverless_function_source_code/"
    serviceAccountFile := "service_account.json"
    serviceAccountFullFile := jsonPath + serviceAccountFile
    os.Setenv("GOOGLE_APPLICATION_CREDENTIALS", serviceAccountFullFile)

    serviceAccountJSON, err := ioutil.ReadFile(serviceAccountFullFile)
    if err != nil {
        log.Fatal(err)
    }

    config, err := google.JWTConfigFromJSON(serviceAccountJSON,
        "https://mail.google.com/", "https://www.googleapis.com/auth/cloud-platform",
    )

    config.Subject = "<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="ddbcb9b0b4b39dbcaabcafb8b3b8a9f3a8ae" rel="noreferrer noopener nofollow">[email protected]</a>"

    ctx := context.Background()
    srv, err := gmail.NewService(ctx, option.WithTokenSource(config.TokenSource(ctx)))
    if err != nil {
        log.Fatalf("Unable to retrieve Gmail client: %v", err)
    }

关于Google Cloud 函数使用服务帐户域范围访问调用 Gmail API,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72918846/

相关文章:

firebase - 在Firebase Cloud Functions项目中生成Swagger文档

android - Gmail 登录集成到 Android 应用程序中,profilePhoto 始终为空?

ios - 从 Gmail 帐户中获取所有电子邮件

go - 如何在golang中获取随机数样本?

json - Golang具有相同json标签名称的多个字段

firebase - Cloud Functions for Firebase 将 promise 数组返回给 GCF

firebase功能配置-删除数据

python - 使用 Google API 获取用户电子邮件的最佳方式是什么?

ssl - 从 HTTP 重定向到 HTTPS 是否有加密?

postgresql - 正确的查询方式来检查凭证是否已经存在