chore: implement 2fa auth (#2968)
Some checks are pending
Build and Release 3X-UI / build (386) (push) Waiting to run
Build and Release 3X-UI / build (amd64) (push) Waiting to run
Build and Release 3X-UI / build (arm64) (push) Waiting to run
Build and Release 3X-UI / build (armv5) (push) Waiting to run
Build and Release 3X-UI / build (armv6) (push) Waiting to run
Build and Release 3X-UI / build (armv7) (push) Waiting to run
Build and Release 3X-UI / build (s390x) (push) Waiting to run

* chore: implement 2fa auth

from #2786

* chore: format code

* chore: replace two factor token input with qr-code

* chore: requesting confirmation of setting/removing two-factor authentication

otpauth library was taken from cdnjs

* chore: revert changes in `ClipboardManager`

don't need it.

* chore: removing twoFactor prop in settings page

* chore: remove `twoFactorQr` object in `mounted` function
This commit is contained in:
Shishkevich D.
2025-05-08 21:20:58 +07:00
committed by GitHub
parent d39ccf4b8f
commit fe3b1c9b52
31 changed files with 452 additions and 302 deletions

View File

@@ -14,9 +14,9 @@ import (
)
type LoginForm struct {
Username string `json:"username" form:"username"`
Password string `json:"password" form:"password"`
LoginSecret string `json:"loginSecret" form:"loginSecret"`
Username string `json:"username" form:"username"`
Password string `json:"password" form:"password"`
TwoFactorCode string `json:"twoFactorCode" form:"twoFactorCode"`
}
type IndexController struct {
@@ -37,7 +37,7 @@ func (a *IndexController) initRouter(g *gin.RouterGroup) {
g.GET("/", a.index)
g.POST("/login", a.login)
g.GET("/logout", a.logout)
g.POST("/getSecretStatus", a.getSecretStatus)
g.POST("/getTwoFactorEnable", a.getTwoFactorEnable)
}
func (a *IndexController) index(c *gin.Context) {
@@ -64,14 +64,13 @@ func (a *IndexController) login(c *gin.Context) {
return
}
user := a.userService.CheckUser(form.Username, form.Password, form.LoginSecret)
user := a.userService.CheckUser(form.Username, form.Password, form.TwoFactorCode)
timeStr := time.Now().Format("2006-01-02 15:04:05")
safeUser := template.HTMLEscapeString(form.Username)
safePass := template.HTMLEscapeString(form.Password)
safeSecret := template.HTMLEscapeString(form.LoginSecret)
if user == nil {
logger.Warningf("wrong username: \"%s\", password: \"%s\", secret: \"%s\", IP: \"%s\"", safeUser, safePass, safeSecret, getRemoteIp(c))
logger.Warningf("wrong username: \"%s\", password: \"%s\", IP: \"%s\"", safeUser, safePass, getRemoteIp(c))
a.tgbot.UserLoginNotify(safeUser, safePass, getRemoteIp(c), timeStr, 0)
pureJsonMsg(c, http.StatusOK, false, I18nWeb(c, "pages.login.toasts.wrongUsernameOrPassword"))
return
@@ -108,8 +107,8 @@ func (a *IndexController) logout(c *gin.Context) {
c.Redirect(http.StatusTemporaryRedirect, c.GetString("base_path"))
}
func (a *IndexController) getSecretStatus(c *gin.Context) {
status, err := a.settingService.GetSecretStatus()
func (a *IndexController) getTwoFactorEnable(c *gin.Context) {
status, err := a.settingService.GetTwoFactorEnable()
if err == nil {
jsonObj(c, status, nil)
}

View File

@@ -19,10 +19,6 @@ type updateUserForm struct {
NewPassword string `json:"newPassword" form:"newPassword"`
}
type updateSecretForm struct {
LoginSecret string `json:"loginSecret" form:"loginSecret"`
}
type SettingController struct {
settingService service.SettingService
userService service.UserService
@@ -44,8 +40,6 @@ func (a *SettingController) initRouter(g *gin.RouterGroup) {
g.POST("/updateUser", a.updateUser)
g.POST("/restartPanel", a.restartPanel)
g.GET("/getDefaultJsonConfig", a.getDefaultXrayConfig)
g.POST("/updateUserSecret", a.updateSecret)
g.POST("/getUserSecret", a.getUserSecret)
}
func (a *SettingController) getAllSetting(c *gin.Context) {
@@ -107,29 +101,6 @@ func (a *SettingController) restartPanel(c *gin.Context) {
jsonMsg(c, I18nWeb(c, "pages.settings.restartPanel"), err)
}
func (a *SettingController) updateSecret(c *gin.Context) {
form := &updateSecretForm{}
err := c.ShouldBind(form)
if err != nil {
jsonMsg(c, I18nWeb(c, "pages.settings.toasts.modifySettings"), err)
}
user := session.GetLoginUser(c)
err = a.userService.UpdateUserSecret(user.Id, form.LoginSecret)
if err == nil {
user.LoginSecret = form.LoginSecret
session.SetLoginUser(c, user)
}
jsonMsg(c, I18nWeb(c, "pages.settings.toasts.modifyUser"), err)
}
func (a *SettingController) getUserSecret(c *gin.Context) {
loginUser := session.GetLoginUser(c)
user := a.userService.GetUserSecret(loginUser.Id)
if user != nil {
jsonObj(c, user, nil)
}
}
func (a *SettingController) getDefaultXrayConfig(c *gin.Context) {
defaultJsonConfig, err := a.settingService.GetDefaultXrayConfig()
if err != nil {