Files
3x-ui/internal/util/totp/totp.go
T
sdhfsl d440c2b932 fix(panel): accept 2FA codes from adjacent TOTP windows (#6546)
* fix(panel): accept 2FA codes from adjacent TOTP windows

CheckUser compared only gotp.Now(), so a code submitted at the end of
its 30s window (or with slight client/server clock drift) failed with
'invalid 2fa code', while the immediate retry in the next window
succeeded. Accept current +/-1 window, the standard TOTP skew
tolerance.

Fixes MHSanaei/3x-ui#6535

* fix(panel): share TOTP skew tolerance with VerifyTwoFactorCode

Move the +/-1 window helper to internal/util/totp so both 2FA
acceptance points use it: login (CheckUser) and disable/rebind plus
username/password changes (VerifyTwoFactorCode). Also shrink comments
to the 2-line house rule and anchor the unit test mid-window to avoid
a step-boundary flake.

Addresses review on #6546 (MEDIUM + 2 LOWs).

---------

Co-authored-by: sdhfsl <sdhfsl@users.noreply.github.com>
2026-09-15 15:42:30 +03:00

23 lines
554 B
Go

package totp
import (
"time"
"github.com/xlzd/gotp"
)
// SkewWindows is how many 30s steps around now VerifyWithSkew accepts.
// Standard TOTP clock-drift tolerance, see MHSanaei/3x-ui#6535.
const SkewWindows = 1
// VerifyWithSkew accepts the code for the current step plus/minus SkewWindows.
func VerifyWithSkew(secret, code string, now time.Time) bool {
totp := gotp.NewDefaultTOTP(secret)
for i := -SkewWindows; i <= SkewWindows; i++ {
if totp.AtTime(now.Add(time.Duration(i*30)*time.Second)) == code {
return true
}
}
return false
}