Skip to content

Commit a20beb3

Browse files
committed
feat(user): 2FA
1 parent 2b58dbf commit a20beb3

33 files changed

+2775
-139
lines changed

app/user/cmd/wire_gen.go

Lines changed: 11 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/user/configs/config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
server:
2+
app: FishPi
23
name: user-service
34
version: 1.0.0
45
mode: dev

app/user/go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ require (
1414
github.com/google/wire v0.7.0
1515
github.com/jinzhu/copier v0.4.0
1616
github.com/lib/pq v1.10.9
17+
github.com/pquerna/otp v1.5.0
1718
github.com/prometheus/client_golang v1.23.2
1819
github.com/qiniu/go-sdk/v7 v7.22.0
1920
github.com/sony/sonyflake/v2 v2.2.0
@@ -39,6 +40,7 @@ require (
3940
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
4041
github.com/beorn7/perks v1.0.1 // indirect
4142
github.com/bmatcuk/doublestar v1.3.4 // indirect
43+
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
4244
github.com/bytedance/gopkg v0.1.3 // indirect
4345
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
4446
github.com/cespare/xxhash/v2 v2.3.0 // indirect

app/user/go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
3131
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
3232
github.com/bmatcuk/doublestar v1.3.4 h1:gPypJ5xD31uhX6Tf54sDPUOBXTqKH4c9aPY66CyQrS0=
3333
github.com/bmatcuk/doublestar v1.3.4/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE=
34+
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=
35+
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
3436
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
3537
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
3638
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
@@ -158,6 +160,8 @@ github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgm
158160
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
159161
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
160162
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
163+
github.com/pquerna/otp v1.5.0 h1:NMMR+WrmaqXU4EzdGJEE1aUUI0AMRzsp96fFFWNPwxs=
164+
github.com/pquerna/otp v1.5.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
161165
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
162166
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
163167
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=

app/user/internal/biz/biz.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ var BizProviderSet = wire.NewSet(
2121
NewAuthenticationDomain,
2222
NewUserDomain,
2323
NewUserRelationDomain,
24+
NewTwoFactorAuthenticationDomain,
2425
)
2526

2627
type BaseDomain struct {

app/user/internal/biz/model/user.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,13 @@ func (u *User) ConvertToRpc() *v1.User {
5858
Province: u.Province,
5959
City: u.City,
6060
PublicLocation: u.PublicLocation,
61-
TwofaSecret: u.TwofaSecret,
61+
TwofaEnable: u.TwofaEnable,
6262
CreatedAt: timestamppb.New(*u.CreatedAt),
6363
UpdatedAt: timestamppb.New(*u.UpdatedAt),
6464
}
65+
if u.TwofaEnable && u.TwofaEnableTime != nil {
66+
reply.TwofaEnableTime = timestamppb.New(*u.TwofaEnableTime)
67+
}
6568
if u.LastLoginTime != nil {
6669
reply.LastLoginTime = timestamppb.New(*u.LastLoginTime)
6770
}

app/user/internal/biz/repo/user.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ type UserRepo interface {
1414
Update(ctx context.Context, tx *gen.Client, u *model.User) (*model.User, error)
1515
UpdateStat(ctx context.Context, tx *gen.Client, userId int64, statType v1.UserStatType, num int32) (*model.User, error)
1616

17+
EnableTwoFactorAuthentication(ctx context.Context, tx *gen.Client, name string, secret string) (int, error)
18+
DisableTwoFactorAuthentication(ctx context.Context, tx *gen.Client, name string) (int, error)
19+
1720
ConstantAccount(ctx context.Context, tx *gen.Client, account string) (bool, error)
1821
GetOne(ctx context.Context, tx *gen.Client, req *UserGetReq) (*model.User, error)
1922
GetByAccount(ctx context.Context, tx *gen.Client, account string) (*model.User, error)
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package biz
2+
3+
import (
4+
"bytes"
5+
cv1 "common/api/common/v1"
6+
"common/pkg/constant"
7+
"context"
8+
"image/png"
9+
"time"
10+
"user/internal/biz/repo"
11+
"user/internal/data/ent"
12+
"user/internal/data/ent/gen"
13+
14+
"github.com/pquerna/otp/totp"
15+
)
16+
17+
type TwoFactorAuthenticationDomain struct {
18+
*BaseDomain
19+
userRepo repo.UserRepo
20+
}
21+
22+
func NewTwoFactorAuthenticationDomain(base *BaseDomain, userRepo repo.UserRepo) (*TwoFactorAuthenticationDomain, error) {
23+
return &TwoFactorAuthenticationDomain{
24+
BaseDomain: base,
25+
userRepo: userRepo,
26+
}, nil
27+
}
28+
29+
func (d *TwoFactorAuthenticationDomain) Validate(ctx context.Context, secret string, code string) bool {
30+
return totp.Validate(code, secret)
31+
}
32+
33+
func (d *TwoFactorAuthenticationDomain) Enable(ctx context.Context, name string) ([]byte, error) {
34+
buf := &bytes.Buffer{}
35+
generate, err := totp.Generate(totp.GenerateOpts{
36+
Issuer: d.conf.Server.App,
37+
AccountName: name,
38+
})
39+
if err != nil {
40+
return nil, err
41+
}
42+
43+
err = d.redis.Client.SetEx(ctx, constant.GetKeyTwoFactorAuthentication(name), generate.Secret(), 5*time.Minute).Err()
44+
if err != nil {
45+
return nil, err
46+
}
47+
48+
image, err := generate.Image(256, 256)
49+
if err != nil {
50+
return nil, err
51+
}
52+
err = png.Encode(buf, image)
53+
if err != nil {
54+
return nil, err
55+
}
56+
return buf.Bytes(), nil
57+
}
58+
59+
func (d *TwoFactorAuthenticationDomain) Disable(ctx context.Context, name string, secret string, code string) error {
60+
if !totp.Validate(code, secret) {
61+
return cv1.ErrorBadRequest("2FA code invalid")
62+
}
63+
err := ent.WithTx(ctx, d.db, func(tx *gen.Client) error {
64+
_, err := d.userRepo.DisableTwoFactorAuthentication(ctx, tx, name)
65+
return err
66+
})
67+
if err != nil {
68+
return err
69+
}
70+
return nil
71+
}
72+
73+
func (d *TwoFactorAuthenticationDomain) Confirm(ctx context.Context, name string, code string) error {
74+
secret, err := d.redis.Client.Get(ctx, constant.GetKeyTwoFactorAuthentication(name)).Result()
75+
if err != nil {
76+
return err
77+
}
78+
if !totp.Validate(code, secret) {
79+
return cv1.ErrorBadRequest("2FA code invalid")
80+
}
81+
err = ent.WithTx(ctx, d.db, func(tx *gen.Client) error {
82+
_, err = d.userRepo.EnableTwoFactorAuthentication(ctx, tx, name, secret)
83+
return err
84+
})
85+
if err != nil {
86+
return err
87+
}
88+
return nil
89+
}

app/user/internal/conf/conf.pb.go

Lines changed: 11 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/user/internal/conf/conf.proto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ message Bootstrap {
1515
}
1616

1717
message Server {
18+
string app = 6;
1819
string name = 1;
1920
string version = 2;
2021
string mode = 3;

0 commit comments

Comments
 (0)