Passed
Pull Request — main (#166)
by Yume
02:10
created

auth.*UseCase.RefreshToken   A

Complexity

Conditions 2

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
nop 2
dl 0
loc 7
rs 10
c 0
b 0
f 0
1
package auth
2
3
import (
4
	"context"
5
	"log/slog"
6
	"strings"
7
8
	"github.com/gofiber/fiber/v2/log"
9
	"github.com/memnix/memnix-rest/domain"
10
	"github.com/memnix/memnix-rest/pkg/jwt"
11
	"github.com/memnix/memnix-rest/services/user"
12
	"github.com/pkg/errors"
13
	"golang.org/x/crypto/bcrypt"
14
	"gorm.io/gorm"
15
)
16
17
// UseCase is the auth use case.
18
type UseCase struct {
19
	user.IRepository
20
}
21
22
// NewUseCase creates a new auth use case.
23
func NewUseCase(repo user.IRepository) IUseCase {
24
	return &UseCase{IRepository: repo}
25
}
26
27
// Login logs in a user
28
// Returns a token and error.
29
func (a *UseCase) Login(ctx context.Context, password string, email string) (string, error) {
30
	userModel, err := a.GetByEmail(ctx, email)
31
	if err != nil {
32
		log.WithContext(ctx).Error("user not found", slog.Any("error", err), slog.String("email", email))
33
		return "", errors.New("user not found")
34
	}
35
36
	ok, err := ComparePasswords(ctx, password, []byte(userModel.Password))
37
	if errors.Is(err, bcrypt.ErrMismatchedHashAndPassword) || !ok {
38
		return "", errors.New("invalid password")
39
	}
40
	if err != nil {
41
		return "", err
42
	}
43
44
	token, err := jwt.GetJwtInstance().GetJwt().GenerateToken(ctx, userModel.ID)
45
	if err != nil {
46
		return "", err
47
	}
48
49
	return token, nil
50
}
51
52
// Register registers a new user
53
// Returns an error.
54
func (a *UseCase) Register(ctx context.Context, registerStruct domain.Register) (domain.User, error) {
55
	if err := VerifyPassword(registerStruct.Password); err != nil {
56
		return domain.User{}, errors.Wrap(err, "Verify password failed")
57
	}
58
59
	hash, err := GenerateEncryptedPassword(ctx, registerStruct.Password)
60
	if err != nil {
61
		return domain.User{}, errors.Wrap(err, "Generate encrypted password failed")
62
	}
63
64
	registerStruct.Password = string(hash)
65
	registerStruct.Email = strings.ToLower(registerStruct.Email)
66
	userModel := registerStruct.ToUser()
67
68
	if err = a.Create(ctx, &userModel); err != nil {
69
		log.WithContext(ctx).Error("failed to create registerStruct in register", slog.Any("error", err))
70
		return domain.User{}, errors.Wrap(err, "failed to create registerStruct in register")
71
	}
72
73
	userModel, err = a.GetByEmail(ctx, registerStruct.Email)
74
	if err != nil {
75
		return domain.User{}, errors.Wrap(err, "failed to get registerStruct in register")
76
	}
77
78
	return userModel, nil
79
}
80
81
// Logout returns an empty string
82
// It might be used to invalidate a token in the future.
83
func (*UseCase) Logout(_ context.Context) (string, error) {
84
	return "", nil
85
}
86
87
// RefreshToken refreshes a token.
88
func (*UseCase) RefreshToken(ctx context.Context, user domain.User) (string, error) {
89
	token, err := jwt.GetJwtInstance().GetJwt().GenerateToken(ctx, user.ID)
90
	if err != nil {
91
		return "", err
92
	}
93
94
	return token, nil
95
}
96
97
// RegisterOauth registers a new user with oauth.
98
func (a *UseCase) RegisterOauth(ctx context.Context, user domain.User) error {
99
	return a.Create(ctx, &user)
100
}
101
102
func (a *UseCase) LoginOauth(ctx context.Context, user domain.User) (string, error) {
103
	userModel, err := a.GetByEmail(ctx, user.Email)
104
	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
105
		log.WithContext(ctx).Error("failed to get user", slog.Any("error", err))
106
		return "", err
107
	}
108
109
	if err == nil && userModel.OauthProvider != user.OauthProvider && userModel.OauthProvider != "" {
110
		log.WithContext(ctx).Warn("user is already registered with another provider")
111
		return "", errors.New("user is already registered with another provider")
112
	}
113
114
	if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
115
		err = a.RegisterOauth(ctx, user)
116
		if err != nil {
117
			log.WithContext(ctx).Error("failed to register user", slog.Any("error", err))
118
			return "", errors.Wrap(err, "failed to register user")
119
		}
120
121
		userModel, err = a.GetByEmail(ctx, user.Email)
122
		if err != nil {
123
			log.WithContext(ctx).Error("failed to get user", slog.Any("error", err))
124
			return "", errors.New("failed to get user")
125
		}
126
	}
127
128
	// Check if user is up to date
129
	if userModel.OauthID == "" {
130
		userModel.OauthID = user.OauthID
131
		userModel.OauthProvider = user.OauthProvider
132
		if user.Avatar != "" {
133
			userModel.Avatar = user.Avatar
134
		}
135
		err = a.Update(ctx, &userModel)
136
		if err != nil {
137
			log.WithContext(ctx).Error("failed to update user", slog.Any("error", err))
138
			return "", errors.Wrap(err, "failed to update user")
139
		}
140
	}
141
142
	return jwt.GetJwtInstance().GetJwt().GenerateToken(ctx, userModel.ID)
143
}
144