// 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/