History

fix(auth): validate antigravity token userinfo email

hkfires committed 9 months ago

Commit e95be101 parent f3d58fa3 files · +57 −32Browse files

Unified · Split

3 files
  1. internal/api/handlers/management/auth_files.go +23 −18
  2. internal/auth/antigravity/auth.go +20 −7
  3. sdk/auth/antigravity.go +14 −7
internal/api/handlers/management/auth_files.go+23−18
@@ -1148,13 +1148,9 @@ func (h *Handler) RequestGeminiCLIToken(c *gin.Context) { · 1147 unmodified lines
1148 }1148 }
1149 1149
1150 ifToken["token_uri"] = "https://oauth2.googleapis.com/token"1150 ifToken["token_uri"] = "https://oauth2.googleapis.com/token"
1151 ifToken["client_id"] = "681255809395-oo8ft2oprdrnp9e3aqf6av3hmdib135j.apps.googleusercontent.com"1151 ifToken["client_id"] = geminiAuth.ClientID
1152 ifToken["client_secret"] = "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"1152 ifToken["client_secret"] = geminiAuth.ClientSecret
1153 ifToken["scopes"] = []string{1153 ifToken["scopes"] = geminiAuth.Scopes
1154 "https://www.googleapis.com/auth/cloud-platform",
1155 "https://www.googleapis.com/auth/userinfo.email",
1156 "https://www.googleapis.com/auth/userinfo.profile",
1157 }
1158 ifToken["universe_domain"] = "googleapis.com"1154 ifToken["universe_domain"] = "googleapis.com"
1159 1155
1160 ts := geminiAuth.GeminiTokenStorage{1156 ts := geminiAuth.GeminiTokenStorage{
@@ -1478,20 +1474,29 @@ func (h *Handler) RequestAntigravityToken(c *gin.Context) { · 317 unmodified lines
1478 return1474 return
1479 }1475 }
1480 1476
1481 email := ""1477 accessToken := strings.TrimSpace(tokenResp.AccessToken)
1482 if strings.TrimSpace(tokenResp.AccessToken) != "" {1478 if accessToken == "" {
1483 fetchedEmail, errInfo := authSvc.FetchUserInfo(ctx, tokenResp.AccessToken)1479 log.Error("antigravity: token exchange returned empty access token")
1484 if errInfo != nil {1480 SetOAuthSessionError(state, "Failed to exchange token")
1485 log.Errorf("Failed to fetch user info: %v", errInfo)1481 return
1486 SetOAuthSessionError(state, "Failed to fetch user info")1482 }
1487 return1483
1488 }1484 email, errInfo := authSvc.FetchUserInfo(ctx, accessToken)
1489 email = strings.TrimSpace(fetchedEmail)1485 if errInfo != nil {
1486 log.Errorf("Failed to fetch user info: %v", errInfo)
1487 SetOAuthSessionError(state, "Failed to fetch user info")
1488 return
1489 }
1490 email = strings.TrimSpace(email)
1491 if email == "" {
1492 log.Error("antigravity: user info returned empty email")
1493 SetOAuthSessionError(state, "Failed to fetch user info")
1494 return
1490 }1495 }
1491 1496
1492 projectID := ""1497 projectID := ""
1493 if strings.TrimSpace(tokenResp.AccessToken) != "" {1498 if accessToken != "" {
1494 fetchedProjectID, errProject := authSvc.FetchProjectID(ctx, tokenResp.AccessToken)1499 fetchedProjectID, errProject := authSvc.FetchProjectID(ctx, accessToken)
1495 if errProject != nil {1500 if errProject != nil {
1496 log.Warnf("antigravity: failed to fetch project ID: %v", errProject)1501 log.Warnf("antigravity: failed to fetch project ID: %v", errProject)
1497 } else {1502 } else {
internal/auth/antigravity/auth.go+20−7
@@ -100,18 +100,19 @@ func (o *AntigravityAuth) ExchangeCodeForTokens(ctx context.Context, code, redir · 99 unmodified lines
100 100
101// FetchUserInfo retrieves user email from Google101// FetchUserInfo retrieves user email from Google
102func (o *AntigravityAuth) FetchUserInfo(ctx context.Context, accessToken string) (string, error) {102func (o *AntigravityAuth) FetchUserInfo(ctx context.Context, accessToken string) (string, error) {
103 if strings.TrimSpace(accessToken) == "" {103 accessToken = strings.TrimSpace(accessToken)
104 return "", nil104 if accessToken == "" {
105 return "", fmt.Errorf("antigravity userinfo: missing access token")
105 }106 }
106 req, err := http.NewRequestWithContext(ctx, http.MethodGet, UserInfoEndpoint, nil)107 req, err := http.NewRequestWithContext(ctx, http.MethodGet, UserInfoEndpoint, nil)
107 if err != nil {108 if err != nil {
108 return "", err109 return "", fmt.Errorf("antigravity userinfo: create request: %w", err)
109 }110 }
110 req.Header.Set("Authorization", "Bearer "+accessToken)111 req.Header.Set("Authorization", "Bearer "+accessToken)
111 112
112 resp, errDo := o.httpClient.Do(req)113 resp, errDo := o.httpClient.Do(req)
113 if errDo != nil {114 if errDo != nil {
114 return "", errDo115 return "", fmt.Errorf("antigravity userinfo: execute request: %w", errDo)
115 }116 }
116 defer func() {117 defer func() {
117 if errClose := resp.Body.Close(); errClose != nil {118 if errClose := resp.Body.Close(); errClose != nil {
@@ -120,13 +121,25 @@ func (o *AntigravityAuth) FetchUserInfo(ctx context.Context, accessToken string) · 2 unmodified lines
120 }()121 }()
121 122
122 if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices {123 if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices {
123 return "", nil124 bodyBytes, errRead := io.ReadAll(io.LimitReader(resp.Body, 8<<10))
125 if errRead != nil {
126 return "", fmt.Errorf("antigravity userinfo: read response: %w", errRead)
127 }
128 body := strings.TrimSpace(string(bodyBytes))
129 if body == "" {
130 return "", fmt.Errorf("antigravity userinfo: request failed: status %d", resp.StatusCode)
131 }
132 return "", fmt.Errorf("antigravity userinfo: request failed: status %d: %s", resp.StatusCode, body)
124 }133 }
125 var info userInfo134 var info userInfo
126 if errDecode := json.NewDecoder(resp.Body).Decode(&info); errDecode != nil {135 if errDecode := json.NewDecoder(resp.Body).Decode(&info); errDecode != nil {
127 return "", errDecode136 return "", fmt.Errorf("antigravity userinfo: decode response: %w", errDecode)
137 }
138 email := strings.TrimSpace(info.Email)
139 if email == "" {
140 return "", fmt.Errorf("antigravity userinfo: response missing email")
128 }141 }
129 return info.Email, nil142 return email, nil
130}143}
131 144
132// FetchProjectID retrieves the project ID for the authenticated user via loadCodeAssist145// FetchProjectID retrieves the project ID for the authenticated user via loadCodeAssist
sdk/auth/antigravity.go+14−7
@@ -153,17 +153,24 @@ waitForCallback: · 152 unmodified lines
153 return nil, fmt.Errorf("antigravity: token exchange failed: %w", errToken)153 return nil, fmt.Errorf("antigravity: token exchange failed: %w", errToken)
154 }154 }
155 155
156 email := ""156 accessToken := strings.TrimSpace(tokenResp.AccessToken)
157 if tokenResp.AccessToken != "" {157 if accessToken == "" {
158 if fetchedEmail, errInfo := authSvc.FetchUserInfo(ctx, tokenResp.AccessToken); errInfo == nil && strings.TrimSpace(fetchedEmail) != "" {158 return nil, fmt.Errorf("antigravity: token exchange returned empty access token")
159 email = strings.TrimSpace(fetchedEmail)159 }
160 }160
161 email, errInfo := authSvc.FetchUserInfo(ctx, accessToken)
162 if errInfo != nil {
163 return nil, fmt.Errorf("antigravity: fetch user info failed: %w", errInfo)
164 }
165 email = strings.TrimSpace(email)
166 if email == "" {
167 return nil, fmt.Errorf("antigravity: empty email returned from user info")
161 }168 }
162 169
163 // Fetch project ID via loadCodeAssist (same approach as Gemini CLI)170 // Fetch project ID via loadCodeAssist (same approach as Gemini CLI)
164 projectID := ""171 projectID := ""
165 if tokenResp.AccessToken != "" {172 if accessToken != "" {
166 fetchedProjectID, errProject := authSvc.FetchProjectID(ctx, tokenResp.AccessToken)173 fetchedProjectID, errProject := authSvc.FetchProjectID(ctx, accessToken)
167 if errProject != nil {174 if errProject != nil {
168 log.Warnf("antigravity: failed to fetch project ID: %v", errProject)175 log.Warnf("antigravity: failed to fetch project ID: %v", errProject)
169 } else {176 } else {