
本文详解如何在 Go 后端正确解析 Google ID Token 并获取用户邮箱、昵称等 Profile 信息,解决直接调用 /userinfo.profile 返回空响应的常见问题。
本文详解如何在 go 后端正确解析 google id token 并获取用户邮箱、昵称等 profile 信息,解决直接调用 `/userinfo.profile` 返回空响应的常见问题。
Google OAuth2 流程中,前端调用 auth2.grantOfflineAccess() 获取授权码(code)后,后端需向 https://www.googleapis.com/oauth2/v4/token 发起 POST 请求换取凭证。该响应不仅包含 access_token,更关键的是返回了 id_token(JWT 格式)——这才是获取用户身份信息的权威凭据。
⚠️ 注意:https://www.googleapis.com/auth/userinfo.profile 是一个无效的 API 端点路径(它实际是 scope 值,非 REST 接口),直接请求将返回空响应或 404。正确做法是:
-
解析
id_token的 payload,提取其中的sub字段(即 Google 用户唯一标识符,也称gplusID); - 使用
access_token调用 Google People API:GET https://people.googleapis.com/v1/people/me?personFields=emailAddresses,names,photos
✅ 推荐使用新版
people.googleapis.com/v1(取代已废弃的plus/v1),无需gplusID路径参数,直接me即可,且支持更细粒度字段控制。
以下是一个完整、健壮的 Go 示例:
import (
"encoding/base64"
"encoding/json"
"fmt"
"strings"
"net/http"
"io/ioutil"
)
// decodeGoogleIDToken 解析 JWT ID Token 的 payload,返回用户唯一 sub(Google ID)
func decodeGoogleIDToken(idToken string) (string, error) {
if idToken == "" {
return "", fmt.Errorf("empty id_token")
}
parts := strings.Split(idToken, ".")
if len(parts) != 3 {
return "", fmt.Errorf("invalid JWT format: expected 3 parts, got %d", len(parts))
}
// Base64URL decode payload (second part)
payload := parts[1]
switch len(payload) % 4 {
case 2:
payload += "=="
case 3:
payload += "="
}
decoded, err := base64.URLEncoding.DecodeString(payload)
if err != nil {
return "", fmt.Errorf("failed to decode JWT payload: %w", err)
}
var claims struct {
Sub string `json:"sub"`
Email string `json:"email"`
Name string `json:"name"`
Picture string `json:"picture"`
}
if err := json.Unmarshal(decoded, &claims); err != nil {
return "", fmt.Errorf("failed to parse JWT claims: %w", err)
}
if claims.Sub == "" {
return "", fmt.Errorf("missing 'sub' claim in ID token")
}
return claims.Sub, nil
}
// fetchUserProfile 通过 access_token 获取用户公开资料(推荐 v1/people/me)
func fetchUserProfile(accessToken string) (map[string]interface{}, error) {
client := &http.Client{}
req, _ := http.NewRequest("GET",
"https://people.googleapis.com/v1/people/me?personFields=names,emailAddresses,photos",
nil,
)
req.Header.Set("Authorization", "Bearer "+accessToken)
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("HTTP request failed: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := ioutil.ReadAll(resp.Body)
return nil, fmt.Errorf("API error %d: %s", resp.StatusCode, string(body))
}
var result map[string]interface{}
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return nil, fmt.Errorf("failed to decode response: %w", err)
}
return result, nil
}
? 关键注意事项:
-
ID Token 必须验证:尽管原文提到“可跳过验证”,但在生产环境绝不建议省略签名与签发者(
iss)、受众(aud)、过期时间(exp)校验。推荐使用google.golang.org/api/idtoken包进行标准验证; - API 启用要求:确保在 Google Cloud Console 中启用了 People API(而非已停用的 Google+ API);
-
OAuth scopes:前端请求时需包含
https://www.googleapis.com/auth/userinfo.email和https://www.googleapis.com/auth/userinfo.profile,否则access_token将无权访问用户资料; - 错误处理:示例中已包含基础错误链路,实际部署应结合日志、重试与监控。
总结:获取 Google 用户资料的核心在于善用 id_token 解析身份标识 + access_token 调用 People API,而非误用 scope URL 作为接口地址。遵循此流程,即可在 Go 服务中安全、可靠地完成用户信息拉取。











