mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-02-18 04:10:51 +08:00
Add Google One personal account login to Gemini CLI OAuth flow: - CLI --login shows mode menu (Code Assist vs Google One) - Web management API accepts project_id=GOOGLE_ONE sentinel - Auto-discover project via onboardUser without cloudaicompanionProject when project is unresolved Improve robustness of auto-discovery and token handling: - Add context-aware auto-discovery polling (30s timeout, 2s interval) - Distinguish network errors from project-selection-required errors - Refresh expired access tokens in readAuthFile before project lookup - Extend project_id auto-fill to gemini auth type (was antigravity-only) Unify credential file naming to geminicli- prefix for both CLI and web. Add extractAccessToken unit tests (9 cases).
88 lines
3.0 KiB
Go
88 lines
3.0 KiB
Go
// Package gemini provides authentication and token management functionality
|
|
// for Google's Gemini AI services. It handles OAuth2 token storage, serialization,
|
|
// and retrieval for maintaining authenticated sessions with the Gemini API.
|
|
package gemini
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/misc"
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// GeminiTokenStorage stores OAuth2 token information for Google Gemini API authentication.
|
|
// It maintains compatibility with the existing auth system while adding Gemini-specific fields
|
|
// for managing access tokens, refresh tokens, and user account information.
|
|
type GeminiTokenStorage struct {
|
|
// Token holds the raw OAuth2 token data, including access and refresh tokens.
|
|
Token any `json:"token"`
|
|
|
|
// ProjectID is the Google Cloud Project ID associated with this token.
|
|
ProjectID string `json:"project_id"`
|
|
|
|
// Email is the email address of the authenticated user.
|
|
Email string `json:"email"`
|
|
|
|
// Auto indicates if the project ID was automatically selected.
|
|
Auto bool `json:"auto"`
|
|
|
|
// Checked indicates if the associated Cloud AI API has been verified as enabled.
|
|
Checked bool `json:"checked"`
|
|
|
|
// Type indicates the authentication provider type, always "gemini" for this storage.
|
|
Type string `json:"type"`
|
|
}
|
|
|
|
// SaveTokenToFile serializes the Gemini token storage to a JSON file.
|
|
// This method creates the necessary directory structure and writes the token
|
|
// data in JSON format to the specified file path for persistent storage.
|
|
//
|
|
// Parameters:
|
|
// - authFilePath: The full path where the token file should be saved
|
|
//
|
|
// Returns:
|
|
// - error: An error if the operation fails, nil otherwise
|
|
func (ts *GeminiTokenStorage) SaveTokenToFile(authFilePath string) error {
|
|
misc.LogSavingCredentials(authFilePath)
|
|
ts.Type = "gemini"
|
|
if err := os.MkdirAll(filepath.Dir(authFilePath), 0700); err != nil {
|
|
return fmt.Errorf("failed to create directory: %v", err)
|
|
}
|
|
|
|
f, err := os.Create(authFilePath)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create token file: %w", err)
|
|
}
|
|
defer func() {
|
|
if errClose := f.Close(); errClose != nil {
|
|
log.Errorf("failed to close file: %v", errClose)
|
|
}
|
|
}()
|
|
|
|
if err = json.NewEncoder(f).Encode(ts); err != nil {
|
|
return fmt.Errorf("failed to write token to file: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// CredentialFileName returns the filename used to persist Gemini CLI credentials.
|
|
// When projectID represents multiple projects (comma-separated or literal ALL),
|
|
// the suffix is normalized to "all" and a "geminicli-" prefix is enforced to keep
|
|
// web and CLI generated files consistent.
|
|
func CredentialFileName(email, projectID string, includeProviderPrefix bool) string {
|
|
email = strings.TrimSpace(email)
|
|
project := strings.TrimSpace(projectID)
|
|
if strings.EqualFold(project, "all") || strings.Contains(project, ",") {
|
|
return fmt.Sprintf("geminicli-%s-all.json", email)
|
|
}
|
|
prefix := ""
|
|
if includeProviderPrefix {
|
|
prefix = "geminicli-"
|
|
}
|
|
return fmt.Sprintf("%s%s-%s.json", prefix, email, project)
|
|
}
|