How to hashPassword in v4? User-permission.services.user.hashPassword method removed in v4?

How do I has a password in Strapi v4? In v3 I used to do it like this:

    const password = await strapi.plugins[
        "users-permissions"
    ].services.user.hashPassword({ password: newPassword });

    await strapi
     .query("user", "users-permissions")
    .update({ id: user.id }, { resetPasswordToken: null, password });

But with v4 strapi.plugins["users-permissions"].services.user no longer has the hashPassword function. Was this moved to somewhere else? I’m really in need to documentation on this topic

I have came up with a solution. For some reason the Strapi team has removed the hashPassword method of the user-permission.user service that looked like this

  hashPassword(user = {}) {
    return new Promise((resolve, reject) => {
      if (!user.password || this.isHashed(user.password)) {
        resolve(null);
      } else {
        bcrypt.hash(`${user.password}`, 10, (err, hash) => {
          if (err) {
            return reject(err);
          }
          resolve(hash);
        });
      }
    });
  },

Some now we need to hash the password ourselves, for that I use the same package as the hashPassword function did (require with const bcrypt = require("bcryptjs");), like this:

  const password = bcrypt.hashSync(newPassword, 10);

  user = await strapi.query("plugin::users-permissions.user").update({
    where: { id: user.id },
    data: { resetPasswordToken: null, password },
  });

I use this to create changePassword function that looks like this:

  async changePassword(ctx) {
    const userId = ctx.request.body.userId;
    const currentPassword = ctx.request.body.currentPassword;
    const newPassword = ctx.request.body.newPassword;

    if (!userId || !currentPassword || !newPassword) {
      return ctx.throw(400, "provide-userId-currentPassword-newPassword");
    }

    let user = await strapi
      .query("plugin::users-permissions.user")
      .findOne({ id: userId });

    const validPassword = await strapi
      .service("plugin::users-permissions.user")
      .validatePassword(currentPassword, user.password);

    if (!validPassword) {
      return ctx.throw(401, "wrong-current-password");
    } else {
      // Generate new hashed password
      const password = bcrypt.hashSync(newPassword, 10);

      user = await strapi.query("plugin::users-permissions.user").update({
        where: { id: user.id },
        data: { resetPasswordToken: null, password },
      });

      // Return new jwt token
      ctx.send({
        jwt: strapi.service("plugin::users-permissions.jwt").issue({
          id: user.id,
        }),
        user: sanitizeOutput(user),
      });
    }
  },
1 Like

I found where the hashPassword function is, so using it is as simple as this:

const auth = require("@strapi/admin/server/services/auth");

async function generateRandomPassword() {
  let pass = '';
  const str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
    'abcdefghijklmnopqrstuvwxyz' +
    '0123456789' +
    '@#$%?!<>';

  for (let i = 1; i <= 16; i++) {
    const char = Math.floor(Math.random() * str.length + 1);
    pass += str.charAt(char)
  }

  return await auth.hashPassword(pass);
}

@JonasJW this avoid something in your solution that I find to be an issue: replicating code from Strapi that they might change in the future.

1 Like

Just ran across this issue as well and dug into it a bit. It looks like they removed that code because the entityService create/update now handles hashing if the field is a password field. See screenshot for details. If you breakpoint on updatePipeline it will return the data hashed. updatePipeline calls into applyTransforms and ultimately uses bcrypt.