fix(auth): validate antigravity token userinfo email

This commit is contained in:
hkfires
2026-01-24 08:33:25 +08:00
parent f3d58fa0ce
commit e95be10485
3 changed files with 57 additions and 32 deletions

View File

@@ -1148,13 +1148,9 @@ func (h *Handler) RequestGeminiCLIToken(c *gin.Context) {
} }
ifToken["token_uri"] = "https://oauth2.googleapis.com/token" ifToken["token_uri"] = "https://oauth2.googleapis.com/token"
ifToken["client_id"] = "681255809395-oo8ft2oprdrnp9e3aqf6av3hmdib135j.apps.googleusercontent.com" ifToken["client_id"] = geminiAuth.ClientID
ifToken["client_secret"] = "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" ifToken["client_secret"] = geminiAuth.ClientSecret
ifToken["scopes"] = []string{ ifToken["scopes"] = geminiAuth.Scopes
"https://www.googleapis.com/auth/cloud-platform",
"https://www.googleapis.com/auth/userinfo.email",
"https://www.googleapis.com/auth/userinfo.profile",
}
ifToken["universe_domain"] = "googleapis.com" ifToken["universe_domain"] = "googleapis.com"
ts := geminiAuth.GeminiTokenStorage{ ts := geminiAuth.GeminiTokenStorage{
@@ -1478,20 +1474,29 @@ func (h *Handler) RequestAntigravityToken(c *gin.Context) {
return return
} }
email := "" accessToken := strings.TrimSpace(tokenResp.AccessToken)
if strings.TrimSpace(tokenResp.AccessToken) != "" { if accessToken == "" {
fetchedEmail, errInfo := authSvc.FetchUserInfo(ctx, tokenResp.AccessToken) log.Error("antigravity: token exchange returned empty access token")
if errInfo != nil { SetOAuthSessionError(state, "Failed to exchange token")
log.Errorf("Failed to fetch user info: %v", errInfo) return
SetOAuthSessionError(state, "Failed to fetch user info") }
return
} email, errInfo := authSvc.FetchUserInfo(ctx, accessToken)
email = strings.TrimSpace(fetchedEmail) if errInfo != nil {
log.Errorf("Failed to fetch user info: %v", errInfo)
SetOAuthSessionError(state, "Failed to fetch user info")
return
}
email = strings.TrimSpace(email)
if email == "" {
log.Error("antigravity: user info returned empty email")
SetOAuthSessionError(state, "Failed to fetch user info")
return
} }
projectID := "" projectID := ""
if strings.TrimSpace(tokenResp.AccessToken) != "" { if accessToken != "" {
fetchedProjectID, errProject := authSvc.FetchProjectID(ctx, tokenResp.AccessToken) fetchedProjectID, errProject := authSvc.FetchProjectID(ctx, accessToken)
if errProject != nil { if errProject != nil {
log.Warnf("antigravity: failed to fetch project ID: %v", errProject) log.Warnf("antigravity: failed to fetch project ID: %v", errProject)
} else { } else {

View File

@@ -100,18 +100,19 @@ func (o *AntigravityAuth) ExchangeCodeForTokens(ctx context.Context, code, redir
// FetchUserInfo retrieves user email from Google // FetchUserInfo retrieves user email from Google
func (o *AntigravityAuth) FetchUserInfo(ctx context.Context, accessToken string) (string, error) { func (o *AntigravityAuth) FetchUserInfo(ctx context.Context, accessToken string) (string, error) {
if strings.TrimSpace(accessToken) == "" { accessToken = strings.TrimSpace(accessToken)
return "", nil if accessToken == "" {
return "", fmt.Errorf("antigravity userinfo: missing access token")
} }
req, err := http.NewRequestWithContext(ctx, http.MethodGet, UserInfoEndpoint, nil) req, err := http.NewRequestWithContext(ctx, http.MethodGet, UserInfoEndpoint, nil)
if err != nil { if err != nil {
return "", err return "", fmt.Errorf("antigravity userinfo: create request: %w", err)
} }
req.Header.Set("Authorization", "Bearer "+accessToken) req.Header.Set("Authorization", "Bearer "+accessToken)
resp, errDo := o.httpClient.Do(req) resp, errDo := o.httpClient.Do(req)
if errDo != nil { if errDo != nil {
return "", errDo return "", fmt.Errorf("antigravity userinfo: execute request: %w", errDo)
} }
defer func() { defer func() {
if errClose := resp.Body.Close(); errClose != nil { if errClose := resp.Body.Close(); errClose != nil {
@@ -120,13 +121,25 @@ func (o *AntigravityAuth) FetchUserInfo(ctx context.Context, accessToken string)
}() }()
if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices { if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices {
return "", nil bodyBytes, errRead := io.ReadAll(io.LimitReader(resp.Body, 8<<10))
if errRead != nil {
return "", fmt.Errorf("antigravity userinfo: read response: %w", errRead)
}
body := strings.TrimSpace(string(bodyBytes))
if body == "" {
return "", fmt.Errorf("antigravity userinfo: request failed: status %d", resp.StatusCode)
}
return "", fmt.Errorf("antigravity userinfo: request failed: status %d: %s", resp.StatusCode, body)
} }
var info userInfo var info userInfo
if errDecode := json.NewDecoder(resp.Body).Decode(&info); errDecode != nil { if errDecode := json.NewDecoder(resp.Body).Decode(&info); errDecode != nil {
return "", errDecode return "", fmt.Errorf("antigravity userinfo: decode response: %w", errDecode)
} }
return info.Email, nil email := strings.TrimSpace(info.Email)
if email == "" {
return "", fmt.Errorf("antigravity userinfo: response missing email")
}
return email, nil
} }
// FetchProjectID retrieves the project ID for the authenticated user via loadCodeAssist // FetchProjectID retrieves the project ID for the authenticated user via loadCodeAssist

View File

@@ -153,17 +153,24 @@ waitForCallback:
return nil, fmt.Errorf("antigravity: token exchange failed: %w", errToken) return nil, fmt.Errorf("antigravity: token exchange failed: %w", errToken)
} }
email := "" accessToken := strings.TrimSpace(tokenResp.AccessToken)
if tokenResp.AccessToken != "" { if accessToken == "" {
if fetchedEmail, errInfo := authSvc.FetchUserInfo(ctx, tokenResp.AccessToken); errInfo == nil && strings.TrimSpace(fetchedEmail) != "" { return nil, fmt.Errorf("antigravity: token exchange returned empty access token")
email = strings.TrimSpace(fetchedEmail) }
}
email, errInfo := authSvc.FetchUserInfo(ctx, accessToken)
if errInfo != nil {
return nil, fmt.Errorf("antigravity: fetch user info failed: %w", errInfo)
}
email = strings.TrimSpace(email)
if email == "" {
return nil, fmt.Errorf("antigravity: empty email returned from user info")
} }
// Fetch project ID via loadCodeAssist (same approach as Gemini CLI) // Fetch project ID via loadCodeAssist (same approach as Gemini CLI)
projectID := "" projectID := ""
if tokenResp.AccessToken != "" { if accessToken != "" {
fetchedProjectID, errProject := authSvc.FetchProjectID(ctx, tokenResp.AccessToken) fetchedProjectID, errProject := authSvc.FetchProjectID(ctx, accessToken)
if errProject != nil { if errProject != nil {
log.Warnf("antigravity: failed to fetch project ID: %v", errProject) log.Warnf("antigravity: failed to fetch project ID: %v", errProject)
} else { } else {