Files
Kiro/internal/auth/user_service.go

101 lines
2.8 KiB
Go

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
}