101 lines
2.8 KiB
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
|
|
}
|