package auth import ( "context" "encoding/json" "fmt" "io" "net/http" "time" "github.com/google/uuid" "golang.org/x/oauth2" "custom-start-page/internal/models" "custom-start-page/internal/storage" ) // GoogleUserInfo represents the user info returned by Google type GoogleUserInfo struct { ID string `json:"id"` Email string `json:"email"` VerifiedEmail bool `json:"verified_email"` Name string `json:"name"` Picture string `json:"picture"` } // UserService handles user creation and retrieval type UserService struct { userRepo *storage.UserRepository } // NewUserService creates a new user service func NewUserService(userRepo *storage.UserRepository) *UserService { return &UserService{ userRepo: userRepo, } } // GetOrCreateUserFromGoogle fetches user info from Google and creates or retrieves the user func (s *UserService) GetOrCreateUserFromGoogle(ctx context.Context, token *oauth2.Token, oauthConfig *oauth2.Config) (*models.User, error) { // Fetch user info from Google userInfo, err := s.fetchGoogleUserInfo(ctx, token, oauthConfig) if err != nil { return nil, fmt.Errorf("failed to fetch Google user info: %w", err) } // Check if user already exists user, err := s.userRepo.GetByOAuthID(ctx, "google", userInfo.ID) if err == nil { // User exists, update last login time user.UpdatedAt = time.Now() if err := s.userRepo.Update(ctx, user); err != nil { return nil, fmt.Errorf("failed to update user: %w", err) } return user, nil } // User doesn't exist, create new user user = &models.User{ ID: uuid.New().String(), Email: userInfo.Email, OAuthProvider: "google", OAuthID: userInfo.ID, CreatedAt: time.Now(), UpdatedAt: time.Now(), } if err := s.userRepo.Create(ctx, user); err != nil { return nil, fmt.Errorf("failed to create user: %w", err) } return user, nil } // fetchGoogleUserInfo fetches user information from Google's userinfo endpoint func (s *UserService) fetchGoogleUserInfo(ctx context.Context, token *oauth2.Token, oauthConfig *oauth2.Config) (*GoogleUserInfo, error) { client := oauthConfig.Client(ctx, token) resp, err := client.Get("https://www.googleapis.com/oauth2/v2/userinfo") if err != nil { return nil, fmt.Errorf("failed to get user info: %w", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { body, _ := io.ReadAll(resp.Body) return nil, fmt.Errorf("failed to get user info: status %d, body: %s", resp.StatusCode, string(body)) } var userInfo GoogleUserInfo if err := json.NewDecoder(resp.Body).Decode(&userInfo); err != nil { return nil, fmt.Errorf("failed to decode user info: %w", err) } if !userInfo.VerifiedEmail { return nil, fmt.Errorf("email not verified") } return &userInfo, nil }