package providers import ( "context" "crypto/rand" "encoding/base64" "encoding/json" "fmt" "net/http" "net/url" "time" ) // GoogleProvider Google OAuth提供者 type GoogleProvider struct { ClientID string ClientSecret string RedirectURI string } // GoogleAuthURLResponse Google授权URL响应 type GoogleAuthURLResponse struct { URL string `json:"url"` State string `json:"state"` Redirect string `json:"redirect,omitempty"` } // GoogleTokenResponse Google Token响应 type GoogleTokenResponse struct { AccessToken string `json:"access_token"` ExpiresIn int `json:"expires_in"` RefreshToken string `json:"refresh_token"` IDToken string `json:"id_token"` TokenType string `json:"token_type"` Scope string `json:"scope"` } // GoogleUserInfo Google用户信息 type GoogleUserInfo struct { ID string `json:"id"` Email string `json:"email"` VerifiedEmail bool `json:"verified_email"` Name string `json:"name"` GivenName string `json:"given_name"` FamilyName string `json:"family_name"` Picture string `json:"picture"` Locale string `json:"locale"` } // NewGoogleProvider 创建Google OAuth提供者 func NewGoogleProvider(clientID, clientSecret, redirectURI string) *GoogleProvider { return &GoogleProvider{ ClientID: clientID, ClientSecret: clientSecret, RedirectURI: redirectURI, } } // GenerateState 生成随机状态码 func (g *GoogleProvider) GenerateState() (string, error) { b := make([]byte, 32) _, err := rand.Read(b) if err != nil { return "", err } return base64.URLEncoding.EncodeToString(b), nil } // GetAuthURL 获取Google授权URL func (g *GoogleProvider) GetAuthURL(state string) (*GoogleAuthURLResponse, error) { authURL := fmt.Sprintf( "https://accounts.google.com/o/oauth2/v2/auth?client_id=%s&redirect_uri=%s&response_type=code&scope=openid+email+profile&state=%s", g.ClientID, url.QueryEscape(g.RedirectURI), state, ) return &GoogleAuthURLResponse{ URL: authURL, State: state, Redirect: g.RedirectURI, }, nil } // ExchangeCode 用授权码换取访问令牌 func (g *GoogleProvider) ExchangeCode(ctx context.Context, code string) (*GoogleTokenResponse, error) { tokenURL := "https://oauth2.googleapis.com/token" data := url.Values{} data.Set("code", code) data.Set("client_id", g.ClientID) data.Set("client_secret", g.ClientSecret) data.Set("redirect_uri", g.RedirectURI) data.Set("grant_type", "authorization_code") client := &http.Client{Timeout: 10 * time.Second} resp, err := postFormWithContext(ctx, client, tokenURL, data) if err != nil { return nil, fmt.Errorf("request failed: %w", err) } defer resp.Body.Close() body, err := readOAuthResponseBody(resp) if err != nil { return nil, fmt.Errorf("read response failed: %w", err) } var tokenResp GoogleTokenResponse if err := json.Unmarshal(body, &tokenResp); err != nil { return nil, fmt.Errorf("parse token response failed: %w", err) } return &tokenResp, nil } // GetUserInfo 获取Google用户信息 func (g *GoogleProvider) GetUserInfo(ctx context.Context, accessToken string) (*GoogleUserInfo, error) { userInfoURL := fmt.Sprintf("https://www.googleapis.com/oauth2/v2/userinfo?access_token=%s", accessToken) req, err := http.NewRequestWithContext(ctx, "GET", userInfoURL, nil) if err != nil { return nil, fmt.Errorf("create request failed: %w", err) } client := &http.Client{Timeout: 10 * time.Second} resp, err := client.Do(req) if err != nil { return nil, fmt.Errorf("request failed: %w", err) } defer resp.Body.Close() body, err := readOAuthResponseBody(resp) if err != nil { return nil, fmt.Errorf("read response failed: %w", err) } var userInfo GoogleUserInfo if err := json.Unmarshal(body, &userInfo); err != nil { return nil, fmt.Errorf("parse user info failed: %w", err) } return &userInfo, nil } // RefreshToken 刷新访问令牌 func (g *GoogleProvider) RefreshToken(ctx context.Context, refreshToken string) (*GoogleTokenResponse, error) { tokenURL := "https://oauth2.googleapis.com/token" data := url.Values{} data.Set("refresh_token", refreshToken) data.Set("client_id", g.ClientID) data.Set("client_secret", g.ClientSecret) data.Set("grant_type", "refresh_token") client := &http.Client{Timeout: 10 * time.Second} resp, err := postFormWithContext(ctx, client, tokenURL, data) if err != nil { return nil, fmt.Errorf("request failed: %w", err) } defer resp.Body.Close() body, err := readOAuthResponseBody(resp) if err != nil { return nil, fmt.Errorf("read response failed: %w", err) } var tokenResp GoogleTokenResponse if err := json.Unmarshal(body, &tokenResp); err != nil { return nil, fmt.Errorf("parse token response failed: %w", err) } return &tokenResp, nil } // ValidateToken 验证访问令牌是否有效 func (g *GoogleProvider) ValidateToken(ctx context.Context, accessToken string) (bool, error) { userInfo, err := g.GetUserInfo(ctx, accessToken) if err != nil { return false, err } return userInfo != nil, nil }