143 lines
4.6 KiB
Go
143 lines
4.6 KiB
Go
package auth
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
|
|
"telesrv/internal/domain"
|
|
"telesrv/internal/otpdelivery"
|
|
"telesrv/internal/store/memory"
|
|
)
|
|
|
|
type testLoginEmailStore struct {
|
|
emails map[string]string
|
|
}
|
|
|
|
func (s *testLoginEmailStore) LoginEmailByPhone(_ context.Context, phone string) (string, bool, error) {
|
|
email, ok := s.emails[domain.NormalizePhone(phone)]
|
|
return email, ok, nil
|
|
}
|
|
|
|
func (s *testLoginEmailStore) SetLoginEmail(_ context.Context, _ int64, _ string) error {
|
|
return nil
|
|
}
|
|
|
|
type testMailSender struct {
|
|
to string
|
|
code string
|
|
}
|
|
|
|
func (s *testMailSender) Deliver(_ context.Context, req otpdelivery.Request) (otpdelivery.Result, error) {
|
|
s.to = req.Recipient
|
|
s.code = req.Code
|
|
return otpdelivery.Result{}, nil
|
|
}
|
|
|
|
func TestConfiguredEmailLoginSendsAndLimitsAttempts(t *testing.T) {
|
|
ctx := context.Background()
|
|
users := memory.NewUserStore()
|
|
authz := memory.NewAuthorizationStore()
|
|
if _, err := users.Create(ctx, domain.User{Phone: "15550009101", FirstName: "Email"}); err != nil {
|
|
t.Fatalf("create user: %v", err)
|
|
}
|
|
emails := &testLoginEmailStore{emails: map[string]string{"15550009101": "alice@example.test"}}
|
|
sender := &testMailSender{}
|
|
appDelivery := &captureLoginCodeDelivery{}
|
|
svc := NewService(users, authz, memory.NewCodeStore(), nil, nil, "12345",
|
|
WithLoginCodeDelivery(appDelivery),
|
|
WithLoginEmail(LoginEmailOptions{
|
|
Enabled: true,
|
|
CodeLength: 6,
|
|
Store: emails,
|
|
Sender: sender,
|
|
}),
|
|
WithCodeMaxAttempts(2))
|
|
|
|
hash, err := svc.SendCode(ctx, "+1 555 000 9101")
|
|
if err != nil {
|
|
t.Fatalf("SendCode: %v", err)
|
|
}
|
|
if sender.to != "alice@example.test" || len(sender.code) != 6 {
|
|
t.Fatalf("sent email to/code = %q/%q, want alice@example.test/6 digits", sender.to, sender.code)
|
|
}
|
|
if len(appDelivery.requests) != 1 || appDelivery.requests[0].PhoneCodeHash != hash || appDelivery.requests[0].Code != sender.code {
|
|
t.Fatalf("App-code delivery=%+v, want same email code/hash", appDelivery.requests)
|
|
}
|
|
delivery, found, err := svc.CodeDelivery(ctx, hash)
|
|
if err != nil || !found {
|
|
t.Fatalf("CodeDelivery found=%v err=%v", found, err)
|
|
}
|
|
if delivery.Kind != domain.AuthCodeDeliveryEmail || delivery.EmailPattern != "a***e@example.test" || delivery.Length != 6 {
|
|
t.Fatalf("delivery = %+v, want email masked length 6", delivery)
|
|
}
|
|
bad1 := wrongCode(sender.code, '0')
|
|
bad2 := wrongCode(sender.code, '1')
|
|
if bad2 == bad1 {
|
|
bad2 = wrongCode(sender.code, '2')
|
|
}
|
|
if _, _, _, err := svc.SignInWithEmail(ctx, domain.Authorization{}, "+15550009101", hash, bad1); !errors.Is(err, ErrCodeInvalid) {
|
|
t.Fatalf("first bad SignInWithEmail err = %v, want ErrCodeInvalid", err)
|
|
}
|
|
if _, _, _, err := svc.SignInWithEmail(ctx, domain.Authorization{}, "+15550009101", hash, bad2); !errors.Is(err, ErrCodeInvalid) {
|
|
t.Fatalf("second bad SignInWithEmail err = %v, want ErrCodeInvalid", err)
|
|
}
|
|
if _, _, _, err := svc.SignInWithEmail(ctx, domain.Authorization{}, "+15550009101", hash, sender.code); !errors.Is(err, ErrCodeExpired) {
|
|
t.Fatalf("SignInWithEmail after max attempts err = %v, want ErrCodeExpired", err)
|
|
}
|
|
}
|
|
|
|
func wrongCode(code string, digit byte) string {
|
|
if code == "" {
|
|
return string(digit)
|
|
}
|
|
out := make([]byte, len(code))
|
|
for i := range out {
|
|
out[i] = digit
|
|
}
|
|
if string(out) != code {
|
|
return string(out)
|
|
}
|
|
for i := range out {
|
|
out[i] = '9'
|
|
}
|
|
return string(out)
|
|
}
|
|
|
|
func TestConfiguredEmailLoginAcceptsCorrectCode(t *testing.T) {
|
|
ctx := context.Background()
|
|
users := memory.NewUserStore()
|
|
authz := memory.NewAuthorizationStore()
|
|
u, err := users.Create(ctx, domain.User{Phone: "15550009102", FirstName: "Email"})
|
|
if err != nil {
|
|
t.Fatalf("create user: %v", err)
|
|
}
|
|
emails := &testLoginEmailStore{emails: map[string]string{"15550009102": "bob@example.test"}}
|
|
sender := &testMailSender{}
|
|
appDelivery := &captureLoginCodeDelivery{}
|
|
var key [8]byte
|
|
key[0] = 0x91
|
|
svc := NewService(users, authz, memory.NewCodeStore(), nil, nil, "12345",
|
|
WithLoginCodeDelivery(appDelivery),
|
|
WithLoginEmail(LoginEmailOptions{
|
|
Enabled: true,
|
|
CodeLength: 5,
|
|
Store: emails,
|
|
Sender: sender,
|
|
}))
|
|
|
|
hash, err := svc.SendCode(ctx, "+15550009102")
|
|
if err != nil {
|
|
t.Fatalf("SendCode: %v", err)
|
|
}
|
|
if len(appDelivery.requests) != 1 || appDelivery.requests[0].Code != sender.code {
|
|
t.Fatalf("App-code delivery=%+v, want same email code", appDelivery.requests)
|
|
}
|
|
got, _, needSignUp, err := svc.SignInWithEmail(ctx, domain.Authorization{AuthKeyID: key}, "+15550009102", hash, sender.code)
|
|
if err != nil {
|
|
t.Fatalf("SignInWithEmail: %v", err)
|
|
}
|
|
if needSignUp || got.ID != u.ID {
|
|
t.Fatalf("SignInWithEmail got user=%d needSignUp=%v, want %d/false", got.ID, needSignUp, u.ID)
|
|
}
|
|
}
|