Initial commit: Custom Start Page application with authentication and DynamoDB storage
This commit is contained in:
100
internal/auth/user_service.go
Normal file
100
internal/auth/user_service.go
Normal file
@@ -0,0 +1,100 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user