better fix

This commit is contained in:
onysd 2026-07-31 16:57:57 +03:00
parent 87a2a2b0e2
commit 84063d75fc
7 changed files with 128 additions and 13 deletions

View file

@ -792,7 +792,7 @@ func (r *Router) onAccountVerifyEmail(ctx context.Context, req *tg.AccountVerify
if ClientTypeFrom(ctx) == ClientTypeAndroid {
return &tg.AccountEmailVerifiedLogin{
Email: email,
SentCode: tgEmailSentCode(p.PhoneCodeHash, domain.MaskEmail(email), len(strings.TrimSpace(code))),
SentCode: tgEmailSentCode(p.PhoneCodeHash, domain.MaskEmail(email), len(strings.TrimSpace(code)), true),
}, nil
}
u, _, needSignUp, signInErr := r.deps.Auth.SignInWithEmail(ctx, r.authzFromCtx(ctx), p.PhoneNumber, p.PhoneCodeHash, code)

View file

@ -36,7 +36,7 @@ func (r *Router) onAccountSendChangePhoneCode(ctx context.Context, req *tg.Accou
return nil, phoneChangeErr(err)
}
if delivery.Kind == domain.AuthCodeDeliveryEmail {
return tgEmailSentCode(hash, delivery.EmailPattern, delivery.Length), nil
return tgEmailSentCode(hash, delivery.EmailPattern, delivery.Length, true), nil
}
return tgSMSSentCode(hash, delivery.Length), nil
}

View file

@ -499,7 +499,7 @@ func tgSMSSentCode(hash string, length int) tg.AuthSentCodeClass {
}
}
func tgEmailSentCode(hash, emailPattern string, length int) tg.AuthSentCodeClass {
func tgEmailSentCode(hash, emailPattern string, length int, resetAvailable bool) tg.AuthSentCodeClass {
if length <= 0 {
length = devCodeLength
}
@ -507,9 +507,14 @@ func tgEmailSentCode(hash, emailPattern string, length int) tg.AuthSentCodeClass
EmailPattern: emailPattern,
Length: length,
}
// reset_available_period=0 表示可立即调用 auth.resetLoginEmail开发环境无等待期
// 让客户端的"无法访问邮箱?"逃生入口可用。
codeType.SetResetAvailablePeriod(0)
if resetAvailable {
// reset_available_period=0 表示可立即调用 auth.resetLoginEmail开发环境无等待期
// 让客户端的"无法访问邮箱?"逃生入口可用。留空(不调用 Set时该入口在客户端
// 完全不显示——见 auth.Service.LoginEmailResetAvailable这个逃生入口本来就
// 走不通(没有真实短信通道,或邮箱本身就是身份、没有"手机"可退回)时,不应该
// 让用户看到一个点了也没用、甚至只会用固定 dev code 顶替的按钮。
codeType.SetResetAvailablePeriod(0)
}
return &tg.AuthSentCode{
Type: codeType,
PhoneCodeHash: hash,
@ -523,6 +528,14 @@ func tgEmailSetupRequiredSentCode(hash string) tg.AuthSentCodeClass {
}
}
// loginEmailResetAvailabilityChecker lets tgSentCodeForHash ask whether
// auth.resetLoginEmail could actually succeed right now, so the client is
// never shown a "Can't access this email?" escape hatch it cannot use (see
// auth.Service.LoginEmailResetAvailable / ConsumeLoginEmailReset).
type loginEmailResetAvailabilityChecker interface {
LoginEmailResetAvailable() bool
}
func (r *Router) tgSentCodeForHash(ctx context.Context, hash string) (tg.AuthSentCodeClass, error) {
if r.deps.Auth == nil {
return tgSentCode(hash), nil
@ -538,7 +551,11 @@ func (r *Router) tgSentCodeForHash(ctx context.Context, hash string) (tg.AuthSen
case domain.AuthCodeDeliverySMS:
return tgSMSSentCode(hash, delivery.Length), nil
case domain.AuthCodeDeliveryEmail:
return tgEmailSentCode(hash, delivery.EmailPattern, delivery.Length), nil
resetAvailable := false
if checker, ok := r.deps.Auth.(loginEmailResetAvailabilityChecker); ok {
resetAvailable = checker.LoginEmailResetAvailable()
}
return tgEmailSentCode(hash, delivery.EmailPattern, delivery.Length, resetAvailable), nil
case domain.AuthCodeDeliveryEmailSetupRequired:
return tgEmailSetupRequiredSentCode(hash), nil
default:

View file

@ -47,6 +47,44 @@ func TestEmailSentCodeUsesDeliveryLength(t *testing.T) {
}
}
// TestEmailSentCodeOmitsResetPeriodWhenUnavailable locks down the client
// signal for "this server can't service auth.resetLoginEmail" (no real SMS
// sender, or login email is this account's actual identity): the flags-
// optional reset_available_period field must be entirely absent, not merely
// 0, since 0 is also what "available right now" looks like on the wire.
func TestEmailSentCodeOmitsResetPeriodWhenUnavailable(t *testing.T) {
for _, tc := range []struct {
name string
resetAvailable bool
}{
{name: "unavailable", resetAvailable: false},
{name: "available", resetAvailable: true},
} {
t.Run(tc.name, func(t *testing.T) {
authSvc := &captureAuthService{
codeDelivery: domain.AuthCodeDelivery{
Kind: domain.AuthCodeDeliveryEmail,
EmailPattern: "a***e@example.test",
Length: 6,
},
resetAvailable: tc.resetAvailable,
}
r := New(Config{}, Deps{Auth: authSvc}, zaptest.NewLogger(t), fixedClock{now: time.Unix(1700000000, 0)})
sent, err := r.tgSentCodeForHash(context.Background(), "hash-email")
if err != nil {
t.Fatalf("tgSentCodeForHash: %v", err)
}
code := sent.(*tg.AuthSentCode)
emailType := code.Type.(*tg.AuthSentCodeTypeEmailCode)
_, ok := emailType.GetResetAvailablePeriod()
if ok != tc.resetAvailable {
t.Fatalf("reset_available_period present = %v, want %v", ok, tc.resetAvailable)
}
})
}
}
func TestAuthSignInRoutesOfficialEmailCodeCarriers(t *testing.T) {
const (
phone = "+86 188 0000 0021"

View file

@ -43,6 +43,11 @@ type captureAuthService struct {
signInWithEmailPhone string
signInWithEmailHash string
signInWithEmailCode string
resetAvailable bool
}
func (s *captureAuthService) LoginEmailResetAvailable() bool {
return s.resetAvailable
}
type blockingUserAuthService struct {