"Se um trabalhador quiser fazer bem o seu trabalho, ele deve primeiro afiar suas ferramentas." - Confúcio, "Os Analectos de Confúcio. Lu Linggong"
Primeira página > Programação > Recurso de redefinição de senha: usando OTP para redefinir a senha

Recurso de redefinição de senha: usando OTP para redefinir a senha

Publicado em 2024-11-06
Navegar:476

Password Reset Feature: Using OTP to Reset Password

Back-end

2. Redefinição de senha

Passando para a próxima API.

COLOQUE em /api/reset-password, req -> otp, email, nova senha, 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)
}

Adicionando rota para ele

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

Agora preciso de duas funções:

  1. VerifiqueOTP -> entrada = otp, email; saída = erro (se houver)
  2. UpdatePassword -> input = email, senha; saída = erro (se houver)
// 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
}

Agora preciso colocar os dois juntos no controlador. Estou usando o bool da função VerifyOTP para indicar se o erro é um erro interno ou é por causa da 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)
}

A API agora está construída e o teste pode ser feito usando o seguinte 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"
}'

Na próxima parte, começarei com o frontend

Declaração de lançamento Este artigo foi reproduzido em: https://dev.to/bitorsic/2-password-reset-feature-using-otp-to-reset-password-3lkj?1 Se houver alguma violação, entre em contato com [email protected] para excluí-lo
Tutorial mais recente Mais>

Isenção de responsabilidade: Todos os recursos fornecidos são parcialmente provenientes da Internet. Se houver qualquer violação de seus direitos autorais ou outros direitos e interesses, explique os motivos detalhados e forneça prova de direitos autorais ou direitos e interesses e envie-a para o e-mail: [email protected]. Nós cuidaremos disso para você o mais rápido possível.

Copyright© 2022 湘ICP备2022001581号-3