diff --git a/pkg/routes/api/v1/login.go b/pkg/routes/api/v1/login.go index 82c2390f..792f137a 100644 --- a/pkg/routes/api/v1/login.go +++ b/pkg/routes/api/v1/login.go @@ -20,11 +20,13 @@ import ( "net/http" "code.vikunja.io/api/pkg/db" - + "code.vikunja.io/api/pkg/log" "code.vikunja.io/api/pkg/models" "code.vikunja.io/api/pkg/modules/auth" + "code.vikunja.io/api/pkg/notifications" user2 "code.vikunja.io/api/pkg/user" "code.vikunja.io/web/handler" + "github.com/golang-jwt/jwt" "github.com/labstack/echo/v4" ) @@ -64,11 +66,23 @@ func Login(c echo.Context) error { } if totpEnabled { + if u.TOTPPasscode == "" { + return handler.HandleHTTPError(user2.ErrInvalidTOTPPasscode{}, c) + } + _, err = user2.ValidateTOTPPasscode(s, &user2.TOTPPasscode{ User: user, Passcode: u.TOTPPasscode, }) if err != nil { + if user2.IsErrInvalidTOTPPasscode(err) { + log.Errorf("Invalid TOTP credentials provided for user %d", user.ID) + + err2 := notifications.Notify(user, &user2.InvalidTOTPNotification{User: user}) + if err2 != nil { + log.Errorf("Could not send failed TOTP notification to user %d: %s", user.ID, err2) + } + } _ = s.Rollback() return handler.HandleHTTPError(err, c) } diff --git a/pkg/user/notifications.go b/pkg/user/notifications.go index d900825e..ba0a9bd8 100644 --- a/pkg/user/notifications.go +++ b/pkg/user/notifications.go @@ -110,3 +110,28 @@ func (n *ResetPasswordNotification) ToDB() interface{} { func (n *ResetPasswordNotification) Name() string { return "" } + +// InvalidTOTPNotification represents a InvalidTOTPNotification notification +type InvalidTOTPNotification struct { + User *User +} + +// ToMail returns the mail notification for InvalidTOTPNotification +func (n *InvalidTOTPNotification) ToMail() *notifications.Mail { + return notifications.NewMail(). + Subject("Someone just tried to login to your Vikunja account, but failed"). + Greeting("Hi "+n.User.GetName()+","). + Line("Someone just tried to log in into your account with correct username and password but a wrong TOTP passcode."). + Line("**If this was not you, someone else knows your password. You should set a new one immediately!**"). + Action("Reset your password", config.ServiceFrontendurl.GetString()+"get-password-reset") +} + +// ToDB returns the InvalidTOTPNotification notification in a format which can be saved in the db +func (n *InvalidTOTPNotification) ToDB() interface{} { + return nil +} + +// Name returns the name of the notification +func (n *InvalidTOTPNotification) Name() string { + return "totp.invalid" +}