在 Go 中验证 Google 登录 ID 令牌

Posted

技术标签:

【中文标题】在 Go 中验证 Google 登录 ID 令牌【英文标题】:Validating Google sign in ID token in Go 【发布时间】:2016-08-11 11:56:45 【问题描述】:

我正在寻找使用 Go 后端服务器项目为 android 的 Google 登录验证 ID 令牌的方法。

在 Go 中使用 Google API 客户端库验证 ID 令牌的等效函数是什么?

从此页面使用 Google API 客户端库部分

https://developers.google.com/identity/sign-in/android/backend-auth#using-a-google-api-client-library

有 Java 和 Python 示例,还有用于验证 ID 令牌的链接,用于使用适用于 php、Node.js 和其他语言的 Google API 客户端库。我检查了我的目标语言;去这里

https://github.com/google/google-api-go-client/blob/master/GettingStarted.md

但是,我发现没有像 Java 和 Python 示例中那样验证令牌的等效函数。 Go 中有什么函数可以做这样的事情吗?

我不想使用令牌信息端点

https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=XYZ123

因为它引入了可能的延迟和网络错误。我希望使用 Google API 客户端库。请指导我应该在哪里查看。

【问题讨论】:

【参考方案1】:

这就是我使用 https://github.com/google/google-api-go-client 库的方式:

import (
    "google.golang.org/api/oauth2/v2"
    "net/http"
)

var httpClient = &http.Client

func verifyIdToken(idToken string) (*oauth2.Tokeninfo, error) 
    oauth2Service, err := oauth2.New(httpClient)
    tokenInfoCall := oauth2Service.Tokeninfo()
    tokenInfoCall.IdToken(idToken)
    tokenInfo, err := tokenInfoCall.Do()
    if err != nil 
        return nil, err
    
    return tokenInfo, nil

oauth2.Tokeninfo 对象包含有关用户的信息。 请注意,这会调用 https://www.googleapis.com/oauth2/v2/tokeninfo,我认为所有 Google API 客户端库都会在后台进行此 http 调用。

【讨论】:

看来我们需要调用 oauth2/v3 而不是 /v2,如果我们仍然需要调用该 url,如何避免延迟和网络问题? google-api-go-client 没有 oauth2/v3,但 v2 运行良好。不知道能不能避免http调用。 oauth2.New(httpClient) 似乎被标记为已弃用以支持oauth2.NewService(ctx, ClientOption) This issue 建议您实际上应该使用 idtoken 包。【参考方案2】:

Google 的 idToken 实际上是 JWT 格式,是紧凑且自包含的带有签名的 JSON。

另请参阅:https://jwt.io/introduction/

google-auth-library-nodejs 的 OAuth2Client.prototype.verifyIdToken 使用 Google 的公钥验证 idtoken 并从 idtoken 中提取 ClaimSet 而无需调用 tokeninfo 端点。

我刚刚从 google-auth-library-nodejs 移植了 verifyIdToken 函数,并为此创建了一个库:https://github.com/futurenda/google-auth-id-token-verifier。

用法:

import (
     "github.com/futurenda/google-auth-id-token-verifier"
)

v := googleAuthIDTokenVerifier.Verifier
aud := "xxxxxx-yyyyyyy.apps.googleusercontent.com"
err := v.VerifyIDToken(TOKEN, []string
    aud,
)
if err == nil 
    claimSet, err := googleAuthIDTokenVerifier.Decode(TOKEN)
    // claimSet.Iss,claimSet.Email ... (See claimset.go)

【讨论】:

【参考方案3】:
import (
    "google.golang.org/api/idtoken"
)

var token string           // this comes from your web or mobile app maybe
const googleClientId = ""  // from credentials in the Google dev console

tokenValidator, err := idtoken.NewValidator(context.Background())
if err != nil 
    // handle error, stop execution


payload, err := tokenValidator.Validate(context.Background(), token, googleClientId)
if err != nil 
    // handle error, stop execution


email := payload.Claims["email"]
name  := payload.Claims["name"]
// and so on...

您可能需要向您的应用程序提供您的 Google 凭据: https://cloud.google.com/docs/authentication/production

【讨论】:

在创建 NewValidator 之前,它希望 GOOGLE_APPLICATION_CREDENTIALS 环境变量设置为从控制台下载的 json 文件。否则, idtoken.NewValidator 会失败,因为它需要一些凭据。我无法让它与 idtoken.Validate 一起使用。我收到此错误:Get "googleapis.com/oauth2/v3/certs": private key should be a PEM or plain PKCS1 or PKCS8;解析错误:asn1:语法错误:序列被截断。有什么想法吗?【参考方案4】:

这很简单,并且有一个单行解决方案。只需使用官方图书馆:

go get google.golang.org/api/idtoken"

然后写下这段代码:

payload, err := idtoken.Validate(context.Background(), request.IdToken, "your google client id")
if err != nil 
    panic(err)

fmt.Print(payload.Claims)

然后你会得到这个输出:

map[
    aud:<Your web application client id>
    azp:<Your android application client id>
    email:<Authenticated user email> 
    email_verified:true
    exp:<expire at>
    family_name:<Authenticated user lastname>
    given_name:<Authenticated user firstname>
    iat:<issued at>
    iss: <accounts.google.com or https://accounts.google.com>
    locale:en
    name:<Authenticated User fullname>
    picture:<Authenticated User Photo URL>
    sub: <Google Account ID [Use this to identify a id uniquely]>
]

【讨论】:

我相信这是新的正确答案。 idtoken 在 Python 等其他语言中是 oauth2 的子包,有点令人困惑,但在 Go 中它是一个单独的库

以上是关于在 Go 中验证 Google 登录 ID 令牌的主要内容,如果未能解决你的问题,请参考以下文章

用于在 GAE 上运行的 Go 中验证 Google 登录令牌的包

Google 登录 - 访问令牌、身份验证令牌和 JWT ID 令牌之间的区别

使用 Google 登录 - 我们如何在 .net 中验证 Google ID 令牌服务器端?缺少代码示例,库似乎已弃用

在 Ruby 中验证 Google ID 令牌的完整性

Android Google Auth 登录获取 Id 令牌 handleSignInResult:false

Android 无效 ID 令牌的 Google 登录