changes for email signup
This commit is contained in:
parent
0fbfc8cd71
commit
3d9b0f1de7
15 changed files with 469 additions and 27 deletions
84
internal/app/auth/email_signup_test.go
Normal file
84
internal/app/auth/email_signup_test.go
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"telesrv/internal/domain"
|
||||
"telesrv/internal/store/memory"
|
||||
)
|
||||
|
||||
func TestEmailSignupSendCodeRoutesFreshSignupToEmail(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
users := memory.NewUserStore()
|
||||
authz := memory.NewAuthorizationStore()
|
||||
sender := &testMailSender{}
|
||||
svc := NewService(users, authz, memory.NewCodeStore(), nil, nil, "12345",
|
||||
WithLoginEmail(LoginEmailOptions{Sender: sender}),
|
||||
WithEmailSignup(true))
|
||||
|
||||
phone, ok := domain.EncodeEmailPhone("newuser@owpengram.local")
|
||||
if !ok {
|
||||
t.Fatalf("EncodeEmailPhone: ok=false")
|
||||
}
|
||||
|
||||
hash, err := svc.SendCode(ctx, phone)
|
||||
if err != nil {
|
||||
t.Fatalf("SendCode: %v", err)
|
||||
}
|
||||
if sender.to != "newuser@owpengram.local" {
|
||||
t.Fatalf("sender.to = %q, want newuser@owpengram.local", sender.to)
|
||||
}
|
||||
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 {
|
||||
t.Fatalf("delivery.Kind = %v, want AuthCodeDeliveryEmail", delivery.Kind)
|
||||
}
|
||||
|
||||
// The email-signup path must reuse the stock auth.signIn/SignInWithEmail
|
||||
// flow unchanged: a fresh (never-registered) 888 phone reports needSignUp.
|
||||
_, _, needSignUp, err := svc.SignInWithEmail(ctx, domain.Authorization{}, phone, hash, sender.code)
|
||||
if err != nil {
|
||||
t.Fatalf("SignInWithEmail: %v", err)
|
||||
}
|
||||
if !needSignUp {
|
||||
t.Fatalf("needSignUp = false, want true for a brand-new email-signup account")
|
||||
}
|
||||
|
||||
// SignUp itself is completely untouched by email-signup: same call, same
|
||||
// phone (the 888-encoded value), no email-specific parameter anywhere.
|
||||
created, _, err := svc.SignUp(ctx, domain.Authorization{}, phone, hash, "New", "User")
|
||||
if err != nil {
|
||||
t.Fatalf("SignUp: %v", err)
|
||||
}
|
||||
if created.Phone != phone {
|
||||
t.Fatalf("created.Phone = %q, want %q", created.Phone, phone)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmailSignupSendCodeIgnoredWhenPhoneIsNotEncoded(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
users := memory.NewUserStore()
|
||||
authz := memory.NewAuthorizationStore()
|
||||
sender := &testMailSender{}
|
||||
svc := NewService(users, authz, memory.NewCodeStore(), nil, nil, "12345",
|
||||
WithLoginEmail(LoginEmailOptions{Sender: sender}),
|
||||
WithEmailSignup(true))
|
||||
|
||||
hash, err := svc.SendCode(ctx, "+15550019999")
|
||||
if err != nil {
|
||||
t.Fatalf("SendCode: %v", err)
|
||||
}
|
||||
if sender.to != "" {
|
||||
t.Fatalf("sender.to = %q, want empty (real phone must fall through to the normal dev-code path)", sender.to)
|
||||
}
|
||||
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 {
|
||||
t.Fatalf("delivery.Kind = Email, want a non-email fallback for a real phone number")
|
||||
}
|
||||
}
|
||||
|
|
@ -86,6 +86,10 @@ type Service struct {
|
|||
loginEmailEnabled bool
|
||||
loginEmailRequireSetup bool
|
||||
loginEmailCodeLength int
|
||||
// emailSignupEnabled 打开后,888 前缀的合成号码(domain.EncodeEmailPhone)
|
||||
// 在 sendCode 阶段直接解码出邮箱、复用登录邮箱同一投递通道发码,与
|
||||
// loginEmailEnabled(手机号账号的第二验证渠道)是两条独立开关。
|
||||
emailSignupEnabled bool
|
||||
// premiumGrantMonths 是新注册账号默认赠送的会员月数;0 表示关闭赠送。
|
||||
premiumGrantMonths int
|
||||
}
|
||||
|
|
@ -181,6 +185,15 @@ func WithLoginEmail(opts LoginEmailOptions) Option {
|
|||
}
|
||||
}
|
||||
|
||||
// WithEmailSignup 打开「邮箱即身份」登录方式:见 emailSignupEnabled 字段注释。
|
||||
// 邮件投递复用 WithLoginEmail 注入的 loginEmailSender,调用方需保证两条配置
|
||||
// 共用同一组 SMTP 设置时任一开关打开都会构造好 sender(见 internal/config 校验)。
|
||||
func WithEmailSignup(enabled bool) Option {
|
||||
return func(s *Service) {
|
||||
s.emailSignupEnabled = enabled
|
||||
}
|
||||
}
|
||||
|
||||
// NewService 创建登录服务。fixedCode 为开发固定验证码。
|
||||
func NewService(users store.UserStore, auths store.AuthorizationStore, codes store.CodeStore, authKeys store.AuthKeyStore, tempKeys store.TempAuthKeyBindingStore, fixedCode string, opts ...Option) *Service {
|
||||
s := &Service{users: users, auths: auths, codes: codes, authKeys: authKeys, tempKeys: tempKeys, fixedCode: fixedCode, codeTTL: 5 * time.Minute, codeMaxAttempts: 5, loginEmailCodeLength: 6}
|
||||
|
|
@ -302,6 +315,11 @@ func (s *Service) SendCode(ctx context.Context, phone string) (string, error) {
|
|||
if found {
|
||||
issuedUserID = existing.ID
|
||||
}
|
||||
if s.emailSignupEnabled {
|
||||
if email, ok := domain.DecodeEmailPhone(phone); ok {
|
||||
return s.createEmailLoginCode(ctx, phone, email, issuedUserID)
|
||||
}
|
||||
}
|
||||
if s.loginEmailEnabled && s.loginEmails != nil {
|
||||
email, found, err := s.loginEmails.LoginEmailByPhone(ctx, phone)
|
||||
if err != nil {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue