feat: PasswordResetBegin of auth controller;

fix: sql query updateLoginInformationByUsername used in-database hashing;
refactor: renamed LogOutAccounts into LogOutSessions in models/auth;
refactor: added error checks on opening transactions for all auth service methods;
refactor: added error checks on commiting transactions likewise;
refactor: simplified PasswordResetBegin logic;
feat: implemented PasswordResetComplete method of auth service;
This commit is contained in:
2025-07-13 19:10:34 +03:00
parent 65ea47dbb6
commit 95294686b7
5 changed files with 208 additions and 34 deletions

View File

@@ -65,7 +65,14 @@ func (a *authServiceImpl) RegistrationBegin(request models.RegistrationBeginRequ
var passwordHash string
var err error
helper, db, _ := database.NewDbHelperTransaction(a.dbctx)
helper, db, err := database.NewDbHelperTransaction(a.dbctx)
if err != nil {
a.log.Error(
"Failed to open a transaction",
zap.Error(err))
return false, errs.ErrServerError
}
defer helper.RollbackOnError(err)
// TODO: check occupation with redis
@@ -184,7 +191,12 @@ func (a *authServiceImpl) RegistrationBegin(request models.RegistrationBeginRequ
zap.String("code", generatedCode))
}
helper.Commit()
if err = helper.Commit(); err != nil {
a.log.Error(
"Failed to commit transaction",
zap.Error(err))
return false, errs.ErrServerError
}
return true, nil
}
@@ -198,7 +210,13 @@ func (a *authServiceImpl) RegistrationComplete(request models.RegistrationComple
var accessToken, refreshToken string
var err error
helper, db, _ := database.NewDbHelperTransaction(a.dbctx)
helper, db, err := database.NewDbHelperTransaction(a.dbctx)
if err != nil {
a.log.Error(
"Failed to open a transaction",
zap.Error(err))
return nil, errs.ErrServerError
}
defer helper.RollbackOnError(err)
user, err = db.TXQueries.GetUserByUsername(db.CTX, request.Username)
@@ -316,7 +334,12 @@ func (a *authServiceImpl) RegistrationComplete(request models.RegistrationComple
return nil, errs.ErrServerError
}
helper.Commit()
if err = helper.Commit(); err != nil {
a.log.Error(
"Failed to commit transaction",
zap.Error(err))
return nil, errs.ErrServerError
}
a.log.Info(
"User verified registration",
@@ -336,7 +359,13 @@ func (a *authServiceImpl) Login(request models.LoginRequest) (*models.LoginRespo
var session database.Session
var err error
helper, db, _ := database.NewDbHelperTransaction(a.dbctx)
helper, db, err := database.NewDbHelperTransaction(a.dbctx)
if err != nil {
a.log.Error(
"Failed to open a transaction",
zap.Error(err))
return nil, errs.ErrServerError
}
defer helper.RollbackOnError(err)
userRow, err = db.TXQueries.GetValidUserByLoginCredentials(db.CTX, database.GetValidUserByLoginCredentialsParams{
@@ -395,7 +424,12 @@ func (a *authServiceImpl) Login(request models.LoginRequest) (*models.LoginRespo
return nil, errs.ErrServerError
}
helper.Commit()
if err = helper.Commit(); err != nil {
a.log.Error(
"Failed to commit transaction",
zap.Error(err))
return nil, errs.ErrServerError
}
response := models.LoginResponse{Tokens: models.Tokens{
AccessToken: accessToken,
@@ -416,6 +450,12 @@ func (a *authServiceImpl) PasswordResetBegin(request models.PasswordResetBeginRe
var err error
helper, db, err := database.NewDbHelperTransaction(a.dbctx)
if err != nil {
a.log.Error(
"Failed to open a transaction",
zap.Error(err))
return false, errs.ErrServerError
}
defer helper.RollbackOnError(err)
ctx := context.TODO()
@@ -427,15 +467,13 @@ func (a *authServiceImpl) PasswordResetBegin(request models.PasswordResetBeginRe
zap.String("email", request.Email),
zap.Error(redisErr))
return false, errs.ErrServerError
}
} else if err == nil {
current_time := time.Now()
if current_time.Unix() < cooldownTimeUnix {
a.log.Warn(
"Attempted to request a new password reset code for email on active reset cooldown",
zap.String("email", request.Email))
return false, errs.ErrTooManyRequests
}
if time.Now().Unix() < cooldownTimeUnix {
a.log.Warn(
"Attempted to request a new password reset code for email on active reset cooldown",
zap.String("email", request.Email))
return false, errs.ErrTooManyRequests
}
if user, err = db.TXQueries.GetUserByEmail(db.CTX, request.Email); err != nil {
@@ -501,13 +539,144 @@ func (a *authServiceImpl) PasswordResetBegin(request models.PasswordResetBeginRe
return false, err
}
helper.Commit()
if err = helper.Commit(); err != nil {
a.log.Error(
"Failed to commit transaction",
zap.Error(err))
return false, errs.ErrServerError
}
return true, nil
}
func (a *authServiceImpl) PasswordResetComplete(request models.PasswordResetCompleteRequest) (*models.PasswordResetCompleteResponse, error) {
return nil, errs.ErrNotImplemented
var resetCode database.ConfirmationCode
var user database.User
var session database.Session
var hashedPassword, accessToken, refreshToken string
var err error
helper, db, err := database.NewDbHelperTransaction(a.dbctx)
if err != nil {
a.log.Error(
"Failed to open a transaction",
zap.Error(err))
return nil, errs.ErrServerError
}
if user, err = db.TXQueries.GetUserByEmail(db.CTX, request.Email); err != nil {
if errors.Is(err, pgx.ErrNoRows) {
a.log.Warn(
"Attempted to complete password reset for unregistered email",
zap.String("email", request.Email),
zap.Error(err))
return nil, errs.ErrForbidden
}
a.log.Error(
"Failed to look up user of email while trying to complete password reset",
zap.String("email", request.Email),
zap.Error(err))
return nil, errs.ErrServerError
}
if resetCode, err = db.TXQueries.GetValidConfirmationCodeByCode(db.CTX, database.GetValidConfirmationCodeByCodeParams{
UserID: user.ID,
CodeType: int32(enums.PasswordResetCodeType),
Code: request.VerificationCode,
}); err != nil {
if errors.Is(err, pgx.ErrNoRows) {
a.log.Warn(
"Attempted to reset password for user using incorrect confirmation code",
zap.String("email", request.Email),
zap.String("username", user.Username),
zap.String("provided_code", request.VerificationCode),
zap.Error(err))
return nil, errs.ErrForbidden
}
}
if err = db.TXQueries.UpdateConfirmationCode(db.CTX, database.UpdateConfirmationCodeParams{
ID: resetCode.ID,
Used: utils.NewPointer(true),
}); err != nil {
a.log.Error(
"Failed to invalidate password reset code upon use",
zap.String("username", user.Username),
zap.String("email", request.Email),
zap.Int64("code_id", resetCode.ID),
zap.Error(err))
return nil, errs.ErrServerError
}
if hashedPassword, err = utils.HashPassword(request.NewPassword); err != nil {
a.log.Error(
"Failed to hash new password as part of user password reset",
zap.String("email", request.Email),
zap.String("username", user.Username),
zap.Error(err))
return nil, errs.ErrServerError
}
if err = db.TXQueries.UpdateLoginInformationByUsername(db.CTX, database.UpdateLoginInformationByUsernameParams{
Username: user.Username,
PasswordHash: hashedPassword,
}); err != nil {
a.log.Error(
"Failed to save new password to database as part of user password reset",
zap.String("username", user.Username),
zap.String("email", request.Email),
zap.Error(err))
}
if request.LogOutSessions {
if err = db.TXQueries.TerminateAllSessionsForUserByUsername(db.CTX, user.Username); err != nil {
a.log.Error(
"Failed to log out older sessions as part of user password reset",
zap.String("email", request.Email),
zap.String("username", user.Username),
zap.Error(err))
return nil, errs.ErrServerError
}
}
if session, err = db.TXQueries.CreateSession(db.CTX, database.CreateSessionParams{
UserID: user.ID,
Name: utils.NewPointer("First device"),
Platform: utils.NewPointer("Unknown"),
LatestIp: utils.NewPointer("Unknown"),
}); err != nil {
a.log.Error(
"Failed to create new session for user as part of user password reset",
zap.String("email", request.Email),
zap.String("username", user.Username),
zap.Error(err))
}
if accessToken, refreshToken, err = utils.GenerateTokens(user.Username, session.Guid.String()); err != nil {
a.log.Error(
"Failed to generate tokens as part of user password reset",
zap.String("email", request.Email),
zap.String("username", user.Username),
zap.Error(err))
return nil, errs.ErrServerError
}
response := models.PasswordResetCompleteResponse{
Tokens: models.Tokens{
AccessToken: accessToken,
RefreshToken: refreshToken,
},
}
if err = helper.Commit(); err != nil {
a.log.Error(
"Failed to commit transaction",
zap.Error(err))
return nil, errs.ErrServerError
}
return &response, nil
}