Skip to content

Commit 3513684

Browse files
committed
[otp] separate package from the auth
1 parent 472b1e2 commit 3513684

File tree

16 files changed

+266
-74
lines changed

16 files changed

+266
-74
lines changed

api/mobile.http

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ Authorization: Bearer {{mobileToken}}
99

1010
###
1111
GET {{baseUrl}}/user/code HTTP/1.1
12-
Authorization: Bearer {{mobileToken}}
12+
Authorization: Basic {{credentials}}
13+
# Authorization: Bearer {{mobileToken}}
1314

1415
###
1516
POST {{baseUrl}}/device HTTP/1.1

configs/config.example.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ cache: # cache config
3838
url: memory:// # cache url (memory:// or redis://) [CACHE__URL]
3939
pubsub: # pubsub config
4040
url: memory:// # pubsub url (memory:// or redis://) [PUBSUB__URL]
41+
otp: # otp config
42+
ttl: 300 # otp ttl in seconds [OTP__TTL]
43+
retries: 3 # otp generation retries (collision handling) [OTP__RETRIES]
4144

4245
## Worker Config ##
4346

internal/config/config.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ type Config struct {
1616
Messages Messages `yaml:"messages"` // messages config
1717
Cache Cache `yaml:"cache"` // cache (memory or redis) config
1818
PubSub PubSub `yaml:"pubsub"` // pubsub (memory or redis) config
19+
OTP OTP `yaml:"otp"` // one-time password config
1920
}
2021

2122
type Gateway struct {
@@ -81,6 +82,11 @@ type PubSub struct {
8182
BufferSize uint `yaml:"buffer_size" envconfig:"PUBSUB__BUFFER_SIZE"`
8283
}
8384

85+
type OTP struct {
86+
TTL uint16 `yaml:"ttl" envconfig:"OTP__TTL"`
87+
Retries uint8 `yaml:"retries" envconfig:"OTP__RETRIES"`
88+
}
89+
8490
func Default() Config {
8591
//nolint:exhaustruct,mnd // default values
8692
return Config{
@@ -113,5 +119,9 @@ func Default() Config {
113119
URL: "memory://",
114120
BufferSize: 128,
115121
},
122+
OTP: OTP{
123+
TTL: 300,
124+
Retries: 3,
125+
},
116126
}
117127
}

internal/config/module.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/android-sms-gateway/server/internal/sms-gateway/modules/messages"
1212
"github.com/android-sms-gateway/server/internal/sms-gateway/modules/push"
1313
"github.com/android-sms-gateway/server/internal/sms-gateway/modules/sse"
14+
"github.com/android-sms-gateway/server/internal/sms-gateway/otp"
1415
"github.com/android-sms-gateway/server/internal/sms-gateway/pubsub"
1516
"github.com/capcom6/go-infra-fx/config"
1617
"github.com/capcom6/go-infra-fx/db"
@@ -132,5 +133,11 @@ func Module() fx.Option {
132133
BufferSize: cfg.PubSub.BufferSize,
133134
}
134135
}),
136+
fx.Provide(func(cfg Config) otp.Config {
137+
return otp.Config{
138+
TTL: time.Duration(cfg.OTP.TTL) * time.Second,
139+
Retries: int(cfg.OTP.Retries),
140+
}
141+
}),
135142
)
136143
}

internal/sms-gateway/app.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"github.com/android-sms-gateway/server/internal/sms-gateway/modules/webhooks"
2020
"github.com/android-sms-gateway/server/internal/sms-gateway/online"
2121
"github.com/android-sms-gateway/server/internal/sms-gateway/openapi"
22+
"github.com/android-sms-gateway/server/internal/sms-gateway/otp"
2223
"github.com/android-sms-gateway/server/internal/sms-gateway/pubsub"
2324
"github.com/android-sms-gateway/server/pkg/health"
2425
"github.com/capcom6/go-infra-fx/cli"
@@ -54,6 +55,7 @@ func Module() fx.Option {
5455
metrics.Module(),
5556
sse.Module(),
5657
online.Module(),
58+
otp.Module(),
5759
)
5860
}
5961

internal/sms-gateway/handlers/middlewares/userauth/userauth.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ func NewCode(authSvc *auth.Service) fiber.Handler {
7272
// Get the code
7373
code := auth[5:]
7474

75-
user, err := authSvc.AuthorizeUserByCode(code)
75+
user, err := authSvc.AuthorizeUserByCode(c.Context(), code)
7676
if err != nil {
7777
return fiber.ErrUnauthorized
7878
}

internal/sms-gateway/handlers/mobile.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ func (h *mobileHandler) patchDevice(device models.Device, c *fiber.Ctx) error {
179179
//
180180
// Get user code.
181181
func (h *mobileHandler) getUserCode(user models.User, c *fiber.Ctx) error {
182-
code, err := h.authSvc.GenerateUserCode(user.ID)
182+
code, err := h.authSvc.GenerateUserCode(c.Context(), user.ID)
183183
if err != nil {
184184
h.Logger.Error("failed to generate user code", zap.Error(err), zap.String("user_id", user.ID))
185185
return fiber.NewError(fiber.StatusInternalServerError, "failed to generate user code")

internal/sms-gateway/modules/auth/module.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ func Module() fx.Option {
1313
fx.Decorate(func(log *zap.Logger) *zap.Logger {
1414
return log.Named("auth")
1515
}),
16-
fx.Provide(New),
1716
fx.Provide(newRepository, fx.Private),
17+
fx.Provide(New),
1818
fx.Invoke(func(lc fx.Lifecycle, svc *Service) {
1919
ctx, cancel := context.WithCancel(context.Background())
2020
lc.Append(fx.Hook{

internal/sms-gateway/modules/auth/service.go

Lines changed: 28 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,15 @@ package auth
22

33
import (
44
"context"
5-
"crypto/rand"
65
"crypto/subtle"
76
"fmt"
87
"time"
98

109
"github.com/android-sms-gateway/server/internal/sms-gateway/models"
1110
"github.com/android-sms-gateway/server/internal/sms-gateway/modules/devices"
1211
"github.com/android-sms-gateway/server/internal/sms-gateway/online"
12+
"github.com/android-sms-gateway/server/internal/sms-gateway/otp"
1313
"github.com/android-sms-gateway/server/pkg/crypto"
14-
"github.com/capcom6/go-helpers/cache"
15-
"github.com/jaevor/go-nanoid"
16-
"go.uber.org/fx"
1714
"go.uber.org/zap"
1815
)
1916

@@ -22,78 +19,50 @@ type Config struct {
2219
PrivateToken string
2320
}
2421

25-
type Params struct {
26-
fx.In
27-
28-
Config Config
29-
30-
Users *repository
31-
DevicesSvc *devices.Service
32-
OnlineSvc online.Service
33-
34-
Logger *zap.Logger
35-
}
36-
3722
type Service struct {
3823
config Config
3924

4025
users *repository
41-
codesCache *cache.Cache[string]
4226
usersCache *usersCache
4327

28+
otpSvc *otp.Service
4429
devicesSvc *devices.Service
4530
onlineSvc online.Service
4631

4732
logger *zap.Logger
48-
49-
idgen func() string
5033
}
5134

52-
func New(params Params) *Service {
53-
const idLen = 21
54-
idgen, _ := nanoid.Standard(idLen)
55-
35+
func New(
36+
config Config,
37+
users *repository,
38+
otpSvc *otp.Service,
39+
devicesSvc *devices.Service,
40+
onlineSvc online.Service,
41+
logger *zap.Logger,
42+
) *Service {
5643
return &Service{
57-
config: params.Config,
58-
users: params.Users,
59-
devicesSvc: params.DevicesSvc,
60-
onlineSvc: params.OnlineSvc,
61-
logger: params.Logger,
62-
idgen: idgen,
63-
64-
codesCache: cache.New[string](cache.Config{TTL: codeTTL}),
65-
usersCache: newUsersCache(),
66-
}
67-
}
44+
config: config,
6845

69-
// GenerateUserCode generates a unique one-time user authorization code.
70-
func (s *Service) GenerateUserCode(userID string) (OneTimeCode, error) {
71-
var code string
72-
var err error
73-
74-
const bytesLen = 3
75-
const maxCode = 1000000
76-
b := make([]byte, bytesLen)
77-
validUntil := time.Now().Add(codeTTL)
78-
for range 3 {
79-
if _, err = rand.Read(b); err != nil {
80-
continue
81-
}
82-
num := (int(b[0]) << 16) | (int(b[1]) << 8) | int(b[2]) //nolint:mnd //bitshift
83-
code = fmt.Sprintf("%06d", num%maxCode)
46+
users: users,
8447

85-
if err = s.codesCache.SetOrFail(code, userID, cache.WithValidUntil(validUntil)); err != nil {
86-
continue
87-
}
48+
otpSvc: otpSvc,
49+
devicesSvc: devicesSvc,
50+
onlineSvc: onlineSvc,
8851

89-
break
52+
logger: logger,
53+
54+
usersCache: newUsersCache(),
9055
}
56+
}
9157

58+
// GenerateUserCode generates a unique one-time user authorization code.
59+
func (s *Service) GenerateUserCode(ctx context.Context, userID string) (*otp.Code, error) {
60+
code, err := s.otpSvc.Generate(ctx, userID)
9261
if err != nil {
93-
return OneTimeCode{}, fmt.Errorf("failed to generate code: %w", err)
62+
return nil, fmt.Errorf("failed to generate code: %w", err)
9463
}
9564

96-
return OneTimeCode{Code: code, ValidUntil: validUntil}, nil
65+
return code, nil
9766
}
9867

9968
func (s *Service) RegisterUser(login, password string) (*models.User, error) {
@@ -176,15 +145,15 @@ func (s *Service) AuthorizeUser(username, password string) (*models.User, error)
176145
}
177146

178147
// AuthorizeUserByCode authorizes a user by one-time code.
179-
func (s *Service) AuthorizeUserByCode(code string) (*models.User, error) {
180-
userID, err := s.codesCache.GetAndDelete(code)
148+
func (s *Service) AuthorizeUserByCode(ctx context.Context, code string) (*models.User, error) {
149+
userID, err := s.otpSvc.Validate(ctx, code)
181150
if err != nil {
182-
return nil, fmt.Errorf("failed to get user by code: %w", err)
151+
return nil, fmt.Errorf("failed to validate code: %w", err)
183152
}
184153

185154
user, err := s.users.GetByID(userID)
186155
if err != nil {
187-
return nil, err
156+
return nil, fmt.Errorf("failed to get user: %w", err)
188157
}
189158

190159
return user, nil
@@ -234,6 +203,5 @@ func (s *Service) Run(ctx context.Context) {
234203
}
235204

236205
func (s *Service) clean(_ context.Context) {
237-
s.codesCache.Cleanup()
238206
s.usersCache.Cleanup()
239207
}
Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,8 @@
11
package auth
22

3-
import "time"
4-
5-
const codeTTL = 5 * time.Minute
6-
73
type Mode string
84

95
const (
106
ModePublic Mode = "public"
117
ModePrivate Mode = "private"
128
)
13-
14-
// OneTimeCode is a one-time user authorization code.
15-
type OneTimeCode struct {
16-
Code string
17-
ValidUntil time.Time
18-
}

0 commit comments

Comments
 (0)