"Si un ouvrier veut bien faire son travail, il doit d'abord affûter ses outils." - Confucius, "Les Entretiens de Confucius. Lu Linggong"
Page de garde > La programmation > Fonction de réinitialisation du mot de passe : utilisation d'OTP pour réinitialiser le mot de passe

Fonction de réinitialisation du mot de passe : utilisation d'OTP pour réinitialiser le mot de passe

Publié le 2024-11-06
Parcourir:965

Password Reset Feature: Using OTP to Reset Password

Back-end

2. Réinitialisation du mot de passe

Passons à l'API suivante.

METTRE sur /api/reset-password, req -> otp, email, nouveau mot de passe, 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)
}

Ajout d'un itinéraire pour celui-ci

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

Maintenant, j'ai besoin de deux fonctions :

  1. VérifierOTP -> input = otp, email ; sortie = erreur (le cas échéant)
  2. UpdatePassword -> input = email, mot de passe ; sortie = erreur (le cas échéant)
// 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
}

Maintenant, je dois les assembler tous les deux dans le contrôleur. J'utilise le booléen de la fonction VerifyOTP pour indiquer si l'erreur est une erreur interne ou si elle est due à l'entrée.

// 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)
}

L'API est maintenant construite et les tests peuvent être effectués à l'aide de la commande cURL suivante

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

Dans la partie suivante, je commencerai par le frontend

Déclaration de sortie Cet article est reproduit sur : https://dev.to/bitorsic/2-password-reset-feature-using-otp-to-reset-password-3lkj?1 En cas d'infraction, veuillez contacter [email protected] pour le supprimer
Dernier tutoriel Plus>

Clause de non-responsabilité: Toutes les ressources fournies proviennent en partie d'Internet. En cas de violation de vos droits d'auteur ou d'autres droits et intérêts, veuillez expliquer les raisons détaillées et fournir une preuve du droit d'auteur ou des droits et intérêts, puis l'envoyer à l'adresse e-mail : [email protected]. Nous nous en occuperons pour vous dans les plus brefs délais.

Copyright© 2022 湘ICP备2022001581号-3