„Wenn ein Arbeiter seine Arbeit gut machen will, muss er zuerst seine Werkzeuge schärfen.“ – Konfuzius, „Die Gespräche des Konfuzius. Lu Linggong“
Titelseite > Programmierung > Funktion zum Zurücksetzen des Passworts: Verwenden von OTP zum Zurücksetzen des Passworts

Funktion zum Zurücksetzen des Passworts: Verwenden von OTP zum Zurücksetzen des Passworts

Veröffentlicht am 06.11.2024
Durchsuche:144

Password Reset Feature: Using OTP to Reset Password

Backend

2. Passwort zurücksetzen

Weiter zur nächsten API.

PUT auf /api/reset-password, req -> otp, email, neues Passwort, 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)
}

Route dafür wird hinzugefügt

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

Jetzt brauche ich zwei Funktionen:

  1. VerifyOTP -> input = otp, email; Ausgabe = Fehler (falls vorhanden)
  2. UpdatePassword -> Eingabe = E-Mail, Passwort; Ausgabe = Fehler (falls vorhanden)
// 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
}

Jetzt muss ich beide im Controller zusammenfügen. Ich verwende die Funktion bool from VerifyOTP, um anzugeben, ob es sich bei dem Fehler um einen internen Fehler handelt oder ob er an der Eingabe liegt.

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

Die API ist jetzt erstellt und der Test kann mit dem folgenden cURL-Befehl durchgeführt werden

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

Im nächsten Teil beginne ich mit dem Frontend

Freigabeerklärung Dieser Artikel ist abgedruckt unter: https://dev.to/bitorsic/2-password-reset-feature-using-otp-to-reset-password-3lkj?1 Bei Verstößen wenden Sie sich bitte an [email protected] um es zu löschen
Neuestes Tutorial Mehr>

Haftungsausschluss: Alle bereitgestellten Ressourcen stammen teilweise aus dem Internet. Wenn eine Verletzung Ihres Urheberrechts oder anderer Rechte und Interessen vorliegt, erläutern Sie bitte die detaillierten Gründe und legen Sie einen Nachweis des Urheberrechts oder Ihrer Rechte und Interessen vor und senden Sie ihn dann an die E-Mail-Adresse: [email protected] Wir werden die Angelegenheit so schnell wie möglich für Sie erledigen.

Copyright© 2022 湘ICP备2022001581号-3