all repos

onasty @ 7cca7d4f9616542688c0814d7f56b53f2689cacc

a one-time notes service

onasty/internal/service/usersrv/oauth.go (view raw)

Smirnov Oleksandr Smirnov Oleksandr
ss2316544@gmail.com
refactor: remove `username` (#112)..., 1 year ago
1
package usersrv
2
3
import (
4
	"context"
5
	"errors"
6
	"log/slog"
7
	"time"
8
9
	"github.com/gofrs/uuid/v5"
10
	"github.com/olexsmir/onasty/internal/dtos"
11
	"github.com/olexsmir/onasty/internal/models"
12
	"github.com/olexsmir/onasty/internal/oauth"
13
)
14
15
var ErrProviderNotSupported = errors.New("oauth2 provider not supported")
16
17
const (
18
	googleProvider = "google"
19
	githubProvider = "github"
20
)
21
22
func (u *UserSrv) GetOAuthURL(providerName string) (string, error) {
23
	switch providerName {
24
	case googleProvider:
25
		return u.googleOauth.GetAuthURL(""), nil
26
	case githubProvider:
27
		return u.githubOauth.GetAuthURL(""), nil
28
	default:
29
		return "", ErrProviderNotSupported
30
	}
31
}
32
33
func (u *UserSrv) HandleOAuthLogin(
34
	ctx context.Context,
35
	providerName, code string,
36
) (dtos.Tokens, error) {
37
	userInfo, err := u.getUserInfoBasedOnProvider(ctx, providerName, code)
38
	if err != nil {
39
		return dtos.Tokens{}, err
40
	}
41
42
	userID, err := u.getUserByOAuthIDOrCreateOne(ctx, userInfo)
43
	if err != nil {
44
		return dtos.Tokens{}, err
45
	}
46
47
	if err = u.userstore.LinkOAuthIdentity(ctx, userID, userInfo.Provider, userInfo.ProviderID); err != nil {
48
		slog.ErrorContext(ctx, "failed to link user identity", "user_id", userID, "err", err)
49
		return dtos.Tokens{}, err
50
	}
51
52
	tokens, err := u.issueTokens(ctx, userID)
53
54
	return tokens, err
55
}
56
57
func (u *UserSrv) getUserInfoBasedOnProvider(
58
	ctx context.Context,
59
	providerName, code string,
60
) (oauth.UserInfo, error) {
61
	var userInfo oauth.UserInfo
62
	var err error
63
64
	switch providerName {
65
	case googleProvider:
66
		userInfo, err = u.googleOauth.ExchangeCode(ctx, code)
67
	case githubProvider:
68
		userInfo, err = u.githubOauth.ExchangeCode(ctx, code)
69
	default:
70
		return oauth.UserInfo{}, ErrProviderNotSupported
71
	}
72
73
	return userInfo, err
74
}
75
76
func (u *UserSrv) getUserByOAuthIDOrCreateOne(
77
	ctx context.Context,
78
	info oauth.UserInfo,
79
) (uuid.UUID, error) {
80
	user, err := u.userstore.GetByOAuthID(ctx, info.Provider, info.ProviderID)
81
	if err != nil {
82
		if errors.Is(err, models.ErrUserNotFound) {
83
			uid, cerr := u.userstore.Create(ctx, models.User{
84
				ID:          uuid.Nil,
85
				Email:       info.Email,
86
				Activated:   true,
87
				Password:    "",
88
				CreatedAt:   time.Now(),
89
				LastLoginAt: time.Now(),
90
			})
91
			return uid, cerr
92
		}
93
		return uuid.Nil, err
94
	}
95
96
	return user.ID, nil
97
}