From 3d51845f57d5670893c336c80c561a64f9496f3c Mon Sep 17 00:00:00 2001 From: Andy Hsu Date: Mon, 13 Nov 2023 15:22:42 +0800 Subject: [PATCH] feat: invalidate old token after changing the password (close #5515) --- internal/model/user.go | 3 +++ server/common/auth.go | 7 +++++-- server/handles/auth.go | 2 +- server/handles/ssologin.go | 4 ++-- server/handles/webauthn.go | 2 +- server/middlewares/auth.go | 12 ++++++++++++ 6 files changed, 24 insertions(+), 6 deletions(-) diff --git a/internal/model/user.go b/internal/model/user.go index 46fe9bd301d..2d61a971c3d 100644 --- a/internal/model/user.go +++ b/internal/model/user.go @@ -4,6 +4,7 @@ import ( "encoding/binary" "encoding/json" "fmt" + "time" "github.com/alist-org/alist/v3/internal/errs" "github.com/alist-org/alist/v3/pkg/utils" @@ -24,6 +25,7 @@ type User struct { ID uint `json:"id" gorm:"primaryKey"` // unique key Username string `json:"username" gorm:"unique" binding:"required"` // username PwdHash string `json:"-"` // password hash + PwdTS int64 `json:"-"` // password timestamp Salt string `json:"-"` // unique salt Password string `json:"password"` // password BasePath string `json:"base_path"` // base path @@ -71,6 +73,7 @@ func (u *User) ValidatePwdStaticHash(pwdStaticHash string) error { func (u *User) SetPassword(pwd string) *User { u.Salt = random.String(16) u.PwdHash = TwoHashPwd(pwd, u.Salt) + u.PwdTS = time.Now().Unix() return u } diff --git a/server/common/auth.go b/server/common/auth.go index 017390bdd7a..b6a79b752aa 100644 --- a/server/common/auth.go +++ b/server/common/auth.go @@ -4,6 +4,7 @@ import ( "time" "github.com/alist-org/alist/v3/internal/conf" + "github.com/alist-org/alist/v3/internal/model" "github.com/golang-jwt/jwt/v4" "github.com/pkg/errors" ) @@ -12,12 +13,14 @@ var SecretKey []byte type UserClaims struct { Username string `json:"username"` + PwdTS int64 `json:"pwd_ts"` jwt.RegisteredClaims } -func GenerateToken(username string) (tokenString string, err error) { +func GenerateToken(user *model.User) (tokenString string, err error) { claim := UserClaims{ - Username: username, + Username: user.Username, + PwdTS: user.PwdTS, RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Duration(conf.Conf.TokenExpiresIn) * time.Hour)), IssuedAt: jwt.NewNumericDate(time.Now()), diff --git a/server/handles/auth.go b/server/handles/auth.go index 37ae736c2ef..209bdd3a2b8 100644 --- a/server/handles/auth.go +++ b/server/handles/auth.go @@ -78,7 +78,7 @@ func loginHash(c *gin.Context, req *LoginReq) { } } // generate token - token, err := common.GenerateToken(user.Username) + token, err := common.GenerateToken(user) if err != nil { common.ErrorResp(c, err, 400, true) return diff --git a/server/handles/ssologin.go b/server/handles/ssologin.go index 89d8ecaf480..52486b97839 100644 --- a/server/handles/ssologin.go +++ b/server/handles/ssologin.go @@ -260,7 +260,7 @@ func OIDCLoginCallback(c *gin.Context) { common.ErrorResp(c, err, 400) } } - token, err := common.GenerateToken(user.Username) + token, err := common.GenerateToken(user) if err != nil { common.ErrorResp(c, err, 400) } @@ -426,7 +426,7 @@ func SSOLoginCallback(c *gin.Context) { return } } - token, err := common.GenerateToken(user.Username) + token, err := common.GenerateToken(user) if err != nil { common.ErrorResp(c, err, 400) } diff --git a/server/handles/webauthn.go b/server/handles/webauthn.go index 952cf480b46..28a89522cfc 100644 --- a/server/handles/webauthn.go +++ b/server/handles/webauthn.go @@ -94,7 +94,7 @@ func FinishAuthnLogin(c *gin.Context) { return } - token, err := common.GenerateToken(user.Username) + token, err := common.GenerateToken(user) if err != nil { common.ErrorResp(c, err, 400, true) return diff --git a/server/middlewares/auth.go b/server/middlewares/auth.go index 14bba5678a1..14f186be8bf 100644 --- a/server/middlewares/auth.go +++ b/server/middlewares/auth.go @@ -57,6 +57,12 @@ func Auth(c *gin.Context) { c.Abort() return } + // validate password timestamp + if userClaims.PwdTS != user.PwdTS { + common.ErrorStrResp(c, "Password has been changed, login please", 401) + c.Abort() + return + } if user.Disabled { common.ErrorStrResp(c, "Current user is disabled, replace please", 401) c.Abort() @@ -105,6 +111,12 @@ func Authn(c *gin.Context) { c.Abort() return } + // validate password timestamp + if userClaims.PwdTS != user.PwdTS { + common.ErrorStrResp(c, "Password has been changed, login please", 401) + c.Abort() + return + } if user.Disabled { common.ErrorStrResp(c, "Current user is disabled, replace please", 401) c.Abort()