package oauth import ( "bookra/apps/auth-service/internal/config" "context" "encoding/json" "fmt" "io" "net/http" "golang.org/x/oauth2" "golang.org/x/oauth2/google" ) type GoogleUser struct { ID string `json:"id"` Email string `json:"email"` Name string `json:"name"` Picture string `json:"picture"` VerifiedEmail bool `json:"verified_email"` } type GoogleProvider struct { config *oauth2.Config } func NewGoogleProvider(cfg *config.Config) *GoogleProvider { if cfg.GoogleClientID == "" || cfg.GoogleClientSecret == "" { return nil } redirectURL := cfg.GoogleRedirectURL if redirectURL == "" { redirectURL = cfg.FrontendURL + "/auth/oauth/google/callback" } return &GoogleProvider{ config: &oauth2.Config{ ClientID: cfg.GoogleClientID, ClientSecret: cfg.GoogleClientSecret, RedirectURL: redirectURL, Scopes: []string{ "openid", "https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile", }, Endpoint: google.Endpoint, }, } } func (p *GoogleProvider) Enabled() bool { return p != nil && p.config != nil } func (p *GoogleProvider) GetAuthURL(state string) string { return p.config.AuthCodeURL(state, oauth2.AccessTypeOnline) } func (p *GoogleProvider) ExchangeCode(ctx context.Context, code string) (*GoogleUser, error) { token, err := p.config.Exchange(ctx, code) if err != nil { return nil, fmt.Errorf("exchange code: %w", err) } client := p.config.Client(ctx, token) resp, err := client.Get("https://www.googleapis.com/oauth2/v2/userinfo") if err != nil { return nil, fmt.Errorf("fetch userinfo: %w", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { body, _ := io.ReadAll(resp.Body) return nil, fmt.Errorf("userinfo returned %d: %s", resp.StatusCode, string(body)) } var user GoogleUser if err := json.NewDecoder(resp.Body).Decode(&user); err != nil { return nil, fmt.Errorf("decode userinfo: %w", err) } return &user, nil } func (p *GoogleProvider) ParseUser(user *GoogleUser) (providerID, email, name string) { return user.ID, user.Email, user.Name }