"Si un trabajador quiere hacer bien su trabajo, primero debe afilar sus herramientas." - Confucio, "Las Analectas de Confucio. Lu Linggong"
Página delantera > Programación > Función de restablecimiento de contraseña: uso de OTP para restablecer la contraseña

Función de restablecimiento de contraseña: uso de OTP para restablecer la contraseña

Publicado el 2024-11-06
Navegar:245

Password Reset Feature: Using OTP to Reset Password

backend

2. Restablecer contraseña

Pasando a la siguiente API.

PONER en /api/reset-password, req -> otp, correo electrónico, nueva contraseña, res -> nocontent

// controllers/passwordReset.go
func ResetPassword(c *fiber.Ctx) error {
    type Input struct {
        OTP         string `json:"otp"`
        Email       string `json:"email"`
        NewPassword string `json:"new_password"`
    }

    var input Input

    err := c.BodyParser(&input)
    if err != nil {
        return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
            "error": "invalid data",
        })
    }

    // no input field should be empty
    if input.OTP == "" || input.Email == "" || input.NewPassword == "" {
        return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
            "error": "invalid data",
        })
    }

    // TODO: check redis for otp and update password

    return c.SendStatus(fiber.StatusNoContent)
}

Agregando ruta para ello

// routes/routes.go
api.Put("/reset-password", controllers.ResetPassword)

Ahora necesito dos funciones:

  1. VerifyOTP -> input = otp, correo electrónico; salida = error (si lo hay)
  2. UpdatePassword -> input = correo electrónico, contraseña; salida = error (si lo hay)
// utils/passwordReset.go
func VerifyOTP(otp string, email string, c context.Context) (error, bool) {
    key := otpKeyPrefix   email

    // get the value for the key
    value, err := config.RedisClient.Get(c, key).Result()
    if err != nil {
        // the following states that the key was not found
        if err == redis.Nil {
            return errors.New("otp expired / incorrect email"), false
        }

        // for other errors
        return err, true
    }

    // compare received otp's hash with value in redis
    err = bcrypt.CompareHashAndPassword([]byte(value), []byte(otp))
    if err != nil {
        return errors.New("incorrect otp"), false
    }

    // delete redis key to prevent abuse of otp
    err = config.RedisClient.Del(c, key).Err()
    if err != nil {
        return err, true
    }

    return nil, false
}

func UpdatePassword(email string, password string, c context.Context) error {
    users := config.DB.Collection("users")

    // hash the password
    hashedPassword, _ := bcrypt.GenerateFromPassword([]byte(password), 10)

    // update the password
    update := bson.M{
        "$set": bson.M{
            "password": hashedPassword,
        },
    }
    _, err := users.UpdateByID(c, email, update)
    if err != nil {
        return err
    }

    return nil
}

Ahora necesito ponerlos a ambos juntos en el controlador. Estoy usando el bool de la función VerifyOTP para indicar si el error es un error interno o se debe a la entrada.

// controllers/passwordReset.go
func ResetPassword(c *fiber.Ctx) error {
    type Input struct {
        OTP         string `json:"otp"`
        Email       string `json:"email"`
        NewPassword string `json:"new_password"`
    }

    var input Input

    err := c.BodyParser(&input)
    if err != nil {
        return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
            "error": "invalid data",
        })
    }

    // no input field should be empty
    if input.OTP == "" || input.Email == "" || input.NewPassword == "" {
        return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
            "error": "invalid data",
        })
    }

    // check redis for otp
    err, isInternalErr := utils.VerifyOTP(input.OTP, input.Email, c.Context())
    if err != nil {
        var code int
        if isInternalErr {
            code = fiber.StatusInternalServerError
        } else {
            code = fiber.StatusUnauthorized
        }

        return c.Status(code).JSON(fiber.Map{
            "error": err.Error(),
        })
    }

    err = utils.UpdatePassword(input.Email, input.NewPassword, c.Context())
    if err != nil {
        return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
            "error": err.Error(),
        })
    }

    return c.SendStatus(fiber.StatusNoContent)
}

La API ya está creada y las pruebas se pueden realizar usando el siguiente comando cURL

curl --location --request PUT 'localhost:3000/api/reset-password' \
--header 'Content-Type: application/json' \
--data-raw '{
    "email": "[email protected]",
    "new_password": "tester123",
    "otp": "DM4RDNF07B"
}'

En la siguiente parte, comenzaré con la interfaz

Declaración de liberación Este artículo se reproduce en: https://dev.to/bitorsic/2-password-reset-feature-using-otp-to-reset-password-3lkj?1 Si hay alguna infracción, comuníquese con [email protected] para borrarlo
Último tutorial Más>

Descargo de responsabilidad: Todos los recursos proporcionados provienen en parte de Internet. Si existe alguna infracción de sus derechos de autor u otros derechos e intereses, explique los motivos detallados y proporcione pruebas de los derechos de autor o derechos e intereses y luego envíelos al correo electrónico: [email protected]. Lo manejaremos por usted lo antes posible.

Copyright© 2022 湘ICP备2022001581号-3