You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Golang使用OAuth2 Service account服务器间认证返回空refresh token如何解决?

问题原因与解决方案

服务账号的服务器到服务器认证模式下,Google OAuth2 不会返回 refresh token——这是设计逻辑决定的。服务账号本身持有完整的凭据(JSON密钥文件),可以直接重新签发新的 access token,不需要依赖 refresh token 完成刷新流程。

你的代码核心问题是一次性获取了 access token 并直接使用,没有利用 Google 提供的 TokenSource 自动刷新机制。TokenSource 内部会自动处理 token 的过期检查与刷新,每次调用 Token() 时都会返回当前有效的 token。

修改后的代码示例

/*
import(
    "context"
    "fmt"
    "log"
    "io/ioutil"

    "github.com/google/go-containerregistry/pkg/authn"
    gcr "github.com/google/go-containerregistry/pkg/name"
    "github.com/google/go-containerregistry/pkg/v1/remote"
    "golang.org/x/oauth2/google"
)
*/

data, err := ioutil.ReadFile(fmt.Sprintf("%s/%s", path, serviceAccountFilePath))
if err != nil {
   log.Fatalf("Failed to read GCP service account key file: %s", err)
}
ctx := context.Background()

// 加载凭据并获取TokenSource
creds, err := google.CredentialsFromJSON(ctx, data, scopes...)
if err != nil {
   log.Fatalf("Failed to load GCP service account credentials: %s", err)
}

// 使用authn.FromTokenSource包装TokenSource,让gcr自动管理token刷新
auth := authn.FromTokenSource(creds.TokenSource)

repo, err := gcr.NewRepository(fmt.Sprintf("%s/%s", urlPrefix, imageName))
if err != nil {
   log.Fatalf("Failed to create repository: %s", err)
}

// 直接使用包装后的auth,remote.List会自动获取有效token
list, err := remote.List(repo, remote.WithAuth(auth))
if err != nil {
   log.Fatalf("Failed to list repository: %s", err)
}

关键说明

  • 不要直接提取单个 AccessToken 保存使用,始终通过 TokenSource 获取 token。
  • authn.FromTokenSource 会将 Google 的 TokenSource 适配为 go-containerregistry 需要的认证接口,每次发起请求时都会自动检查 token 是否过期,过期则自动生成新的 token。
  • 服务账号的 token 有效期固定为1小时,TokenSource 会在 token 即将过期时自动刷新,无需手动干预。

内容的提问来源于stack exchange,提问作者secmohammed

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 07:20:36